I came up with this issue when i added /admin
endpoint to antMatchers("/admin").hasAuthority("ADMIN")
it simply won't make a GET request to /admin
and return 200
it returns 403
instead
Note: i'm using JWT as an extra layer of authentication.
This is my security config
httpSecurity.csrf().disable()
.authorizeRequests().antMatchers("/", "/health", "/authority", "/dashboard", "/users/login", "/logoutUser", "/manageEvents", "/manageAeds", "/manageReports",
"/charts", "/error", "/profile", "/authenticate/**", "/login", "/403", "/userProfile", "/deleteAed", "/users/add").permitAll()
.antMatchers("/admin").hasAuthority("ADMIN")
.antMatchers("/css/**", "/img/**", "/js/**", "/images/**", "/error_css/**", "/scss/**", "/vendor/**").permitAll()
.anyRequest().authenticated().and().
exceptionHandling().accessDeniedPage("/403").and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
By moving it to
permit.All()
it will work but it's not the case here.
This is where i handle the redirect inside @Controller
@GetMapping("/authority")
public String getAuth(HttpServletResponse response) {
if (jwt == null) {
return "redirect:/login";
}
if (jwtTokenUtil.isTokenExpired(jwt)) {
return "redirect:/login?token=expired";
}
response.addHeader("Auth", "Bearer " + jwt);
System.out.println(loggedinUser.getRoles());
if (loggedinUser != null) {
if (loggedinUser.getRoles().equalsIgnoreCase("TILEFONITIS")) {
return "redirect:/dashboard"; //will redirect
} else if (loggedinUser.getRoles().equalsIgnoreCase("ADMIN")) {
System.out.println("Admin");
return "redirect:/admin"; //won't redirect
} else if (loggedinUser.getRoles().equalsIgnoreCase("GUEST")) {
return "redirect:/403"; // will redirect
} else {
return "redirect:/dashboard"; // will redirect
}
} else {
return "redirect:/login";
}
}
and this is my /admin
inside @Controller
which is never called.
@GetMapping("/admin")
public String getAdmin(HttpServletResponse response) {
if (jwt == null) {
return "redirect:/login";
}
if (jwtTokenUtil.isTokenExpired(jwt)) {
return "redirect:/login?token=expired";
}
response.addHeader("Auth", "Bearer " + jwt);
System.out.println("jwt" + jwt);
return "admin";
}
The odd thing is that with Postman i get redirected!
What am i missing here?
Edit: The first call is at /authenticate/web
where i tell spring i'm authenticated
authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(auth.getUsername(), auth.getPassword()));
Edit 2:
To make things even clearer:
Visiting from web, flow:
- POST
/authenticate/web
- redirect with
.js
to/authority
(GET) - Won't redirect to
/admin
(GET) -> 403
Visiting from Postman, flow:
- POST
/authenticate/web
- Get the
JWT
and include it in headers and make a GET to/authority
- I'm seeing the admin template. -> 200
That's really odd, i add the jwt
every time with response.addHeader
on the web flow .
Update:
- These are the response headers from postman:
plus the JWT
.
- Response headers from the web
Although now i noticed i get 302 from the web instead of a 200
and as you can see admin
page is 403
Update 2:
I've managed to break down a few things, first of all
by having a
httpSecurity.addFilterBefore
on my security configuration means spring will look for theJWT
and add a filter before the position of the specified filter classauthorities are correctly assigned to users, so there is no issue there
i changed
hasAuthority()
tohasRole()
If you get the current user you can automatically access it's authority as shown below
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println("Principal: "+auth.getPrincipal());
System.out.println("Authorities: "+auth.getAuthorities());
Because the
authentication
is overriden by thejwt filter
this means i will only manage to get a user only if the request header contains the validjwt
this is why it worked from postman
but not from the web
.
Another issue is that in my controller i'm trying to add the jwt
on the response
header which will be added to it only when the controller finishes it's job, i can't get in the very next line the user principal becuase there is no jwt
to it's request header.
This screenshot represents a web call and the call from postman where both access /authority
endpoint.
- From postman you see
ADMIN
as an authority - But from the web i have a
ROLE_ANONYMOUS
So i have two options to solve this:
- Add it to the request header.
- Protect
REST
endpoints withJWT
and use default spring security (hasRole()
etc) for the web part.