I have implemented SpringSecurity with JWT, all working nicely except one thing
@PreAuthorize("hasAuthority('admin:read')")
isnt working, it gives
403 FORBIDDEN error
I will share parts of important codes. The token return all authorities properly which I added as SimpleGrantedAuthority
SecurityConfig
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...logic
}
AccountController
@RestController
@RequestMapping("/api")
public class AccountController {
//this works
@RequestMapping(value = "/accounts", method = RequestMethod.GET)
@PreAuthorize("hasAnyRole('ROLE_ADMIN')")
public ResponseEntity<?> loadAll() {
List<AccountDTO> res = accountService.loadAll();
return new ResponseEntity(new ResponseWrapper(res), HttpStatus.OK);
}
//this doesn't work
@RequestMapping(value = "/accounts/{uid}", method = RequestMethod.GET)
@PreAuthorize("hasAuthority('admin:read')")
ResponseEntity<?> findByUId(@PathVariable String uid) throws Exception {
AccountDTO dto = this.accountService.findByUid(uid);
return new ResponseEntity<>(new ResponseWrapper(dto), HttpStatus.OK);
}
}
Token
{
"sub": "nenad.arbutina",
"authorities": [
{
"authority": "ROLE_ADMIN"
},
{
"authority": "admin:read"
},
{
"authority": "admin:create"
},
{
"authority": "admin:update"
},
{
"authority": "admin:delete"
}
],
"iat": 1631111042,
"exp": 1632261600
}
update on code. When debugged I am getting only ROLE_ADMIN, so I am confused
SecurityConfig
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final MyUserDetailService myUserDetailService;
private final SecretKey secretKey;
private final JwtConfig jwtConfig;
@Autowired
public SecurityConfig(
MyUserDetailService myUserDetailService,
SecretKey secretKey,
JwtConfig jwtConfig) {
this.myUserDetailService = myUserDetailService;
this.secretKey = secretKey;
this.jwtConfig = jwtConfig;
}
@Bean
PasswordEncoder getEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(new JwtUsernameAndPasswordAuthenticationFilter(authenticationManager(), jwtConfig, secretKey))
.addFilterAfter(new JwtTokenVerifier(secretKey, jwtConfig), JwtUsernameAndPasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/", "index","/css/*", "/js/*").permitAll()
.anyRequest()
.authenticated();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
@Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new
DaoAuthenticationProvider();
provider.setPasswordEncoder(getEncoder());
provider.setUserDetailsService(myUserDetailService);
return provider;
}
}
JwtTokenVerifier
public class JwtTokenVerifier extends OncePerRequestFilter {
private final SecretKey secretKey;
private final JwtConfig jwtConfig;
public JwtTokenVerifier(SecretKey secretKey,
JwtConfig jwtConfig) {
this.secretKey = secretKey;
this.jwtConfig = jwtConfig;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String authorizationHeader = request.getHeader(jwtConfig.getAuthorizationHeader());
if (Strings.isNullOrEmpty(authorizationHeader) || !authorizationHeader.startsWith(jwtConfig.getTokenPrefix())) {
filterChain.doFilter(request, response);
return;
}
String token = authorizationHeader.replace(jwtConfig.getTokenPrefix(), "");
try {
Jws<Claims> claimsJws = Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token);
Claims body = claimsJws.getBody();
String username = body.getSubject();
List<Map<String, String>> authorities = (List<Map<String, String>>) body.get("authorities");
Set<SimpleGrantedAuthority> simpleGrantedAuthorities = authorities.stream()
.map(m -> new SimpleGrantedAuthority(m.get("authority")))
.collect(Collectors.toSet());
Authentication authentication = new UsernamePasswordAuthenticationToken(
username,
null,
simpleGrantedAuthorities
);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
} catch (JwtException e ){
throw new IllegalStateException(String.format("Token %s is not to be trusted", token));
}
}
}