SUVAM ROY
SUVAM ROY

Reputation: 123

Confusion In Spring Security in Spring Boot

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 extends OnceperRequestFilter where there is a doFilter 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 in UsernamePasswordAuthenticationToken.

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 of UsernamePasswordAuthenticationToken but against what it is validating "upat"? Is UsernamePasswordAuthenticationToken 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

Answers (1)

Marcus Hert da Coregio
Marcus Hert da Coregio

Reputation: 6278

  1. 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;

  2. 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

Related Questions