使用JWT / antMatchers阻止访问的Spring安全配置

时间:2015-09-24 13:04:08

标签: java spring spring-mvc spring-security jwt

我们在1.3 Spring Boot应用程序中设置Spring Security。我们创建了一个使用Java配置来配置所有内容的类,但出于某种原因,每当我尝试访问配置为“permitAll()”的任何URL时,我都会收到与此类似的消息响应:

{
  "timestamp": 1443099232454,
  "status": 403,
  "error": "Forbidden",
  "message": "Access Denied",
  "path": "/api/register"
}

如果我设置antMatchers以允许访问注册,身份验证和激活网址,我不确定为什么会这样做。如果我禁用这三行,我可以访问这三个端点。

这是我目前的配置:

SecurityConfig.java

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Inject
    private Http401UnauthorizedEntryPoint authenticationEntryPoint;

    @Inject
    private UserDetailsService userDetailsService;

    @Inject
    private TokenProvider tokenProvider;

    public SecurityConfig() {
        super(true);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Inject
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http
            .exceptionHandling()
            .authenticationEntryPoint(authenticationEntryPoint)
        .and()
            .csrf()
            .disable()
            .headers()
            .frameOptions()
            .disable()
        .and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
            .authorizeRequests()
            .antMatchers("/api/register").permitAll()
            .antMatchers("/api/activate").permitAll()
            .antMatchers("/api/authenticate").permitAll()
        .and()
            .authorizeRequests()
            .anyRequest()
            .authenticated()
        .and()
            .apply(securityConfigurerAdapter());
        // @formatter:on
    }

    private JwtTokenConfigurer securityConfigurerAdapter() {
        return new JwtTokenConfigurer(tokenProvider);
    }
}

UserDetailsS​​ervice.java

@Service("userDetailsService")
@Log4j2
public class UserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {

    @Inject
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(final String email) {
        log.debug("Authenticating {}", email);
        String lowercaseEmail = email.toLowerCase();
        Optional<User> userFromDatabase = userRepository.findOneByEmail(lowercaseEmail);
        return userFromDatabase.map(
                user -> {
                    if (!user.isEnabled()) {
                        throw new DisabledException("User " + lowercaseEmail + " is disabled");
                    }

                    List<GrantedAuthority> grantedAuthorities = user.getRoles().stream()
                            .map(role -> role.getGrantedAuthority()).collect(Collectors.toList());

                    return new org.springframework.security.core.userdetails.User(lowercaseEmail, user.getPassword(),
                        grantedAuthorities);
                }).orElseThrow(
                () -> new UsernameNotFoundException("User " + lowercaseEmail + " was not found in the database"));
    }
}

JwtTokenConfigurer.java

public class JwtTokenConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

    private TokenProvider tokenProvider;

    public JwtTokenConfigurer(TokenProvider tokenProvider) {
        this.tokenProvider = tokenProvider;
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        JwtTokenFilter customFilter = new JwtTokenFilter(tokenProvider);
        http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

JwtTokenFilter.java

public class JwtTokenFilter extends GenericFilterBean {
    private final static String JWT_TOKEN_HEADER_NAME = "Authorization";
    private TokenProvider tokenProvider;

    public JwtTokenFilter(TokenProvider tokenProvider) {
        this.tokenProvider = tokenProvider;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
        throws IOException, ServletException {
        try {
            HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
            String jwtToken = httpServletRequest.getHeader(JWT_TOKEN_HEADER_NAME);

            if (StringUtils.hasText(jwtToken)) {
                String authorizationSchema = "Bearer";
                if (jwtToken.indexOf(authorizationSchema) == -1) {
                    throw new InsufficientAuthenticationException("Authorization schema not found");
                }
                jwtToken = jwtToken.substring(authorizationSchema.length()).trim();

                JwtClaims claims = tokenProvider.parseToken(jwtToken);
                String email = (String) claims.getClaimValue(TokenConstants.EMAIL.name());
                List<GrantedAuthority> grantedAuthorities = claims.getStringListClaimValue(TokenConstants.ROLES.name())
                    .stream().map(role -> new SimpleGrantedAuthority(role)).collect(Collectors.toList());

                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
                    email, null, grantedAuthorities);
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
            filterChain.doFilter(servletRequest, servletResponse);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }
}

Http401UnauthorizedEntryPoint.java

@Component
public class Http401UnauthorizedEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException arg2)
        throws IOException, ServletException {
        log.debug("Pre-authenticated entry point called. Rejecting access");
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied");
    }
}

正如我之前提到的,每次我尝试访问这三个端点中的任何一个时:

.antMatchers("/api/register").permitAll()
.antMatchers("/api/activate").permitAll()
.antMatchers("/api/authenticate").permitAll()

我拒绝访问...有什么想法吗?

1 个答案:

答案 0 :(得分:1)

您需要允许匿名用户。

@Override
protected void configure(HttpSecurity http) throws Exception {
    // @formatter:off
    http
        .exceptionHandling()
        .authenticationEntryPoint(authenticationEntryPoint)
    .and()
        .csrf()
        .disable()
        .headers()
        .frameOptions()
        .disable()
    .and()
        .sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    .and()
        .authorizeRequests()
        .antMatchers("/api/register").permitAll()
        .antMatchers("/api/activate").permitAll()
        .antMatchers("/api/authenticate").permitAll()
    .and()
        .anonymous()
        .authorizeRequests()
        .anyRequest()
        .authenticated()
    .and()
        .apply(securityConfigurerAdapter());
    // @formatter:on
}

因为AbstractSecurityInterceptor总是询问SecurityContextHolder中是否存在某些内容。 AbstractSecurityInterceptor#beforeInvocation第221行

if (SecurityContextHolder.getContext().getAuthentication() == null) {
 credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound","An Authentication object was not found in the SecurityContext"),object, attributes);

}