使用 JWT / antMatchers 阻止访问的 Spring 安全配置
Posted
技术标签:
【中文标题】使用 JWT / antMatchers 阻止访问的 Spring 安全配置【英文标题】:Spring security configuration with JWT / antMatchers blocking access 【发布时间】:2015-12-22 02:26:24 【问题描述】:我们正在 1.3 Spring Boot 应用程序中设置 Spring Security。我们创建了一个类来使用 Java config 配置所有内容,但由于某种原因,每次我尝试访问任何配置为“permitAll()”的 URL 时,我都会收到与此类似的消息响应:
"timestamp": 1443099232454,
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/api/register"
如果我将 antMatchers 设置为允许访问注册、身份验证和激活 url,我不确定为什么会得到这个。如果我禁用这三行,我就可以访问这三个端点。
这是我当前的配置:
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);
UserDetailsService.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()
我的访问被拒绝...有什么想法吗?
【问题讨论】:
你能发布你的 /api/register 控制器吗?检查 FilterSecurityInterceptor 中发生了什么。似乎您正在请求对 /api/register 进行某种授权。也许在 Controller 类中使用 PreAuthorize 或 Secured 注释,这对所有方法都有效。 【参考方案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);
【讨论】:
以上是关于使用 JWT / antMatchers 阻止访问的 Spring 安全配置的主要内容,如果未能解决你的问题,请参考以下文章
尝试使用 express-jwt 阻止访问除登录页面以外的 Angular 应用程序时出现问题
安全配置不允许我在某些页面上使用 antMatchers()