I recently ran in an issue at work:
Using a role-based access control with Spring Security, how can I refresh
user B’s authorities whenuser Amodifiesuser B’s roles?
You’d think that this would be done automatically, but unfortunately, it isn’t. The authorities are decided upon authentication, meaning that until the user re-authenticates, his authorities won’t necessarily reflect the current access he should be having.
I searched quite a bit for an easy answer, and there were many posts who were left unanswered, or even worse, answers from XML-based Spring configuration.
Ugh, XML.
Anyhow, I ended up having to figure it out on my own: just force each users to re-authenticate every time they make a request. By using an interceptor, we can do just that! The downside is that well, obviously, if you use a database to get the roles of your users, you’ll have to query your database every time there’s a request.
Luckily for me (and unluckily for you, probably), performance was not a concern, so I could ignore the performance impact.
First, create an interceptor:
@Component
public class VerifyAccessInterceptor implements HandlerInterceptor {
// ...
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Set<GrantedAuthority> authorities = new HashSet<>();
if (auth.isAuthenticated()) {
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
}
User userFromDatabase = getUserFromDatabase(auth.getName());
if (userFromDatabase != null) {
// add whatever authorities you want here
authorities.add(new SimpleGrantedAuthority("..."));
}
Authentication newAuth = null;
if (auth.getClass() == OAuth2AuthenticationToken.class) {
OAuth2User principal = ((OAuth2AuthenticationToken)auth).getPrincipal();
if (principal != null) {
newAuth = new OAuth2AuthenticationToken(principal, authorities,(((OAuth2AuthenticationToken)auth).getAuthorizedClientRegistrationId()));
}
}
SecurityContextHolder.getContext().setAuthentication(newAuth);
return true;
}
}
My specific implementation uses OAuth2 (OAuth2AuthenticationToken), but you can use UsernamePasswordAuthenticationToken instead.
And now, to add your interceptor to the configuration:
@Configuration
public class WebConfiguration extends WebMvcConfigurationSupport {
@Autowired
private VerifyAccessInterceptor verifyAccessInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(verifyAccessInterceptor).addPathPatterns("/**");
}
}
Obviously, this is just a very rough implementation. It works as-is, but that it shouldn’t be given a bit more love. A few enhancements can be made, such as:
Authentication object