我们在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);
}
}
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()
我拒绝访问...有什么想法吗?
答案 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);
}