Reputation: 123
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
JWTAuthenticationFilter
that extendsOnceperRequestFilter
where there is adoFilter
method 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
setDetails
method 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 ofUsernamePasswordAuthenticationToken
but against what it is validating "upat"? IsUsernamePasswordAuthenticationToken
automatically 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.
Upvotes: 4
Views: 4118
Reputation: 6278
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.
Upvotes: 3