I have a custom UserDetailService implementor i.e. MyCustomUserDetailService where loadUserByName method is overridden.
I have token generator class that generates the JWT token.
I have a class called
JWTAuthenticationFilterthat extendsOnceperRequestFilterwhere there is adoFiltermethod where I am validating the token that has been generated and provided by the client in the header of the HttpRequest. Now from here is my real question starts. The code is something like this:
UserDetails usd=this.myCustomUserDetailService.loadByUserName(userNameFromToke);//userNameFromToke is a String that got extracted from the token that client provided.
UsernamePasswordAuthenticationToken upat=new UsernamePasswordAuthenticationToken(usd,null,usd.getAuthorities()); //Here in the password I am passing null.
upat.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
SecurityContextHolder.getContext().setAuthentication(upat);
Question 1:
`ups.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));`why we are passing the whole HTTP Request(i.e.httpRequest) rather than the userdetails that we extracted from the token? And why do I need this
setDetailsmethod as I have already passed the userdetails,pasword and the list of authorities in the constructor argument inUsernamePasswordAuthenticationToken.Question 2:
How is the setAuthentication method and UsernamePasswordAuthenticationToken working? I am passing the information that I have extracted from the token i.e.
SecurityContextHolder.getContext().setAuthentication(upat); and userdetails,passowords and authorities in the constructor ofUsernamePasswordAuthenticationTokenbut against what it is validating "upat"? IsUsernamePasswordAuthenticationTokenautomatically internally checking with the "MyCustomUserDetailService"?
I know the question is a bit lengthy but as I am a beginner I am trying hard to understand the mechanism behind it and I have done research on it but this confusion are not getting away. Please help. Also, suggest me how can I concise my question if you seem it's faulty.
The details in the UsernamePasswordAuthenticationToken are totally up to you, it helps if you need more details from the authenticated user later on in your application. Like when you call SecurityContextHolder.getContext().getAuthentication(), you can cast this Authentication to UsernamePasswordAuthenticationToken and access its details for any business purposes;
Your UsernamePasswordAuthenticationToken is not being validated against anything, you need to do it manually by either calling your UserDetailsService and checking the password, or injecting and calling AuthenticationManager#authenticate method. Spring Security does not care how the SecurityContextHolder was populated. In basic scenarios, after you setting an authenticated SecurityContext into the SecurityContextHolder, the SecurityContextPersistenceFilter will take this SecurityContext and save it in the HttpSession as an attribute.
In the next requests, this attribute will be present in your HttpSession, this way Spring Security loads the SecurityContext and sets it into the SecurityContextHolder.
You can get more details in the SecurityContextPersistenceFilter implementation, but I'll point out the specific part for this question here:
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
// Here Spring Security loads the SecurityContext from the HttpSession, this is done before reaching your controllers, before calling the FilterChain.
SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);
try {
SecurityContextHolder.setContext(contextBeforeChainExecution);
if (contextBeforeChainExecution.getAuthentication() == null) {
logger.debug("Set SecurityContextHolder to empty SecurityContext");
}
else {
if (this.logger.isDebugEnabled()) {
this.logger
.debug(LogMessage.format("Set SecurityContextHolder to %s", contextBeforeChainExecution));
}
}
chain.doFilter(holder.getRequest(), holder.getResponse());
}
finally {
// You've populated the SecurityContextHolder and it'll be available here after you application returns. So it'll get the SecurityContext and persist it in the HttpSession
SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
SecurityContextHolder.clearContext();
// Persisting the SecurityContext in the HttpSession
this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
request.removeAttribute(FILTER_APPLIED);
this.logger.debug("Cleared SecurityContextHolder to complete request");
}
I hope this helps you to understand better how Spring Security works. There is also a reference documentation that is a great source to know more about the details of the architecture.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With