Spring @Secured 和 @PreAuthorize 不能正常工作(总是状态 403)
Posted
技术标签:
【中文标题】Spring @Secured 和 @PreAuthorize 不能正常工作(总是状态 403)【英文标题】:Spring @Secured and @PreAuthorize doesn`t work properly(always status 403) 【发布时间】:2020-08-27 08:45:11 【问题描述】:我的 Spring REST 应用程序具有 JWT 授权。在我使用 @Secured 或 @PreAuthorize("hasRole('ROLE_SOME')") 注释控制器中的某些方法之前,一切正常。在这种情况下,每次向这些方法发送请求时,我都会得到状态 403。我的代码有什么问题?
网络安全
@Configuration
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter
private UserDetailsServiceImpl userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserDetailsServiceImpl userDetailsService,
BCryptPasswordEncoder bCryptPasswordEncoder
)
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
@Override
protected void configure(HttpSecurity http) throws Exception
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.antMatchers(HttpMethod.GET,"/api/person/activate/*").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
@Bean
CorsConfigurationSource corsConfigurationSource()
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
我的用户主体
public class MyUserPrincipal implements UserDetails
private Person user;
MyUserPrincipal(Person user)
this.user = user;
@Override
public Collection<? extends GrantedAuthority> getAuthorities()
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority(user.getRole().getName()));
return authorities;
@Override
public String getPassword()
return user.getPassword();
@Override
public String getUsername()
return user.getEmail();
@Override
public boolean isAccountNonExpired()
return true;
@Override
public boolean isAccountNonLocked()
return true;
@Override
public boolean isCredentialsNonExpired()
return true;
@Override
public boolean isEnabled()
return user.isEnabled();
UserDetailsServiceImpl
@Service
public class UserDetailsServiceImpl implements UserDetailsService
private PersonRepo personRepo;
private static final Logger logger = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
public UserDetailsServiceImpl(PersonRepo personRepo)
this.personRepo = personRepo;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException
Person person = personRepo.findByEmail(email);
if (person != null)
logger.debug("role " + person.getRole().getName());
return new MyUserPrincipal(person);
else
logger.error("user not found or inactive : " + person.getEmail());
throw new UsernameNotFoundException(email);
方法安全配置
@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration
JWTAuthenticationFilter
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter
private AuthenticationManager authenticationManager;
private static final Logger logger = LoggerFactory.getLogger(JWTAuthenticationFilter.class);
public JWTAuthenticationFilter(AuthenticationManager authenticationManager)
this.authenticationManager = authenticationManager;
@Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException
try
Person creds = new ObjectMapper()
.readValue(req.getInputStream(), Person.class);
logger.debug("login attempt " + creds.getEmail());
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getEmail(),
creds.getPassword(),
new ArrayList<>())
);
catch (IOException e)
logger.error("unsuccessful attempt " + e);
throw new RuntimeException(e);
@Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth)
String token = JWT.create()
.withSubject(((UserDetails) auth.getPrincipal()).getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.sign(HMAC512(SECRET.getBytes()));
logger.debug("successful log in, token : " + token);
res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
JWTAuthorizationFilter
public class JWTAuthorizationFilter extends BasicAuthenticationFilter
JWTAuthorizationFilter(AuthenticationManager authManager)
super(authManager);
@Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX))
chain.doFilter(req, res);
return;
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request)
String token = request.getHeader(HEADER_STRING);
if (token != null)
String user = JWT.require(Algorithm.HMAC512(SECRET.getBytes()))
.build()
.verify(token.replace(TOKEN_PREFIX, ""))
.getSubject();
if (user != null)
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
return null;
return null;
【问题讨论】:
【参考方案1】:在 MyUserPrincipal 类中,我找到了以下行。
authorities.add(new SimpleGrantedAuthority(user.getRole().getName()));
能否请您检查一下角色是如何保存在数据库中的。由于您有 @PreAuthorize("hasRole('ROLE_SOME')") 有时如果您的角色未在数据库中保存为 ROLE_SOME ,您可能会收到 403 错误。
如果您在数据库中的角色在数据库中保存为“SOME”,请尝试更改以下代码。
authorities.add(new SimpleGrantedAuthority("ROLE_" + user.getRole().getName()));
【讨论】:
以上是关于Spring @Secured 和 @PreAuthorize 不能正常工作(总是状态 403)的主要内容,如果未能解决你的问题,请参考以下文章
spring security 3 中的@Secured 和@PreAuthorize 有啥区别?
Spring Security 3.2:不考虑@Secured注解
Spring Security,注释@Secured 不起作用