vidy
vidy

Reputation: 1640

JWT authentication with spring and angular with null header

I am trying to do JWT token authentication using spring boot and angular. After login bearer token is created but after that in JWTAuthorizationFilter i am getting null header and because of that it return anonymousUser. Please tell me why i am getting null header.

SecurityConfig.java

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;
    
    @Autowired
    private CustomUserDetailService customUserDetailService;
     
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception { 
         http.
         cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())
        .and().csrf().disable()
                .authorizeRequests()                                                                
                .antMatchers("/**").permitAll()                  
                .antMatchers("/manage/**").hasRole("ADMIN")                                      
                .antMatchers("/").hasRole("USER")
                .and()
                .exceptionHandling()
                .accessDeniedPage("/access-denied")
                .and()
                .addFilter(new JWTAuthenticationFilter(authenticationManager()))
                .addFilter(new JWTAuthorizationFilter(authenticationManager(), customUserDetailService));
    }
     
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(customUserDetailService).passwordEncoder(new 
   
        BCryptPasswordEncoder());
    }
}

JWTAuthenticationFilter.java

    public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    private AuthenticationManager authenticationManager;

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        try {
            UserDetail user = new ObjectMapper().readValue(request.getInputStream(), UserDetail.class);
            return this.authenticationManager
                    .authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {
        String username = ((org.springframework.security.core.userdetails.User) authResult.getPrincipal()).getUsername();
        String token = Jwts
                .builder()
                .setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .compact();
        String bearerToken = TOKEN_PREFIX + token;
        System.out.println(bearerToken);
        response.getWriter().write(bearerToken);
        response.addHeader(HEADER_STRING, bearerToken);
       }
    }

JWTAuthorizationFilter.java

    public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
    private final CustomUserDetailService customUserDetailService;

    public JWTAuthorizationFilter(AuthenticationManager authenticationManager, CustomUserDetailService customUserDetailService) {
        super(authenticationManager);
        this.customUserDetailService = customUserDetailService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws IOException, ServletException {
        String header = request.getHeader(HEADER_STRING);
        if (header == null || !header.startsWith(TOKEN_PREFIX)) {
            chain.doFilter(request, response);
            return;
        }
        UsernamePasswordAuthenticationToken authenticationToken = getAuthenticationToken(request);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getAuthenticationToken(HttpServletRequest request) {
        String token = request.getHeader(HEADER_STRING);
        if (token == null) return null;
        String username = Jwts.parser().setSigningKey(SECRET)
                .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
                .getBody()
                .getSubject();
        UserDetails userDetails = customUserDetailService.loadUserByUsername(username);
        return username != null ?
                new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()) 
      : null;
      }
      }

CustomUserDetailService.java

@Component
public class CustomUserDetailService implements UserDetailsService {

    private List<GrantedAuthority> role;

    @Autowired
    private UserDAO userDAO;

    /*
     * @Autowired public CustomUserDetailService(UserRepository userRepository) {
     * this.userRepository = userRepository; }
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        User user = Optional.ofNullable(userDAO.getByEmail(username))
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));
        List<GrantedAuthority> authorityListAdmin = AuthorityUtils.createAuthorityList("ROLE_ADMIN");
        List<GrantedAuthority> authorityListUser = AuthorityUtils.createAuthorityList("ROLE_USER");
        if (user.getRole() == "admin") {
            role = authorityListAdmin;
        } else {
            role = authorityListUser;
        }
        return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), role);
    }
}

Userinfo.java

private String email;
private String role;
private String password;

Controller

@RequestMapping(value = "/login")
public ModelAndView login(
        @RequestParam(name = "error", required = false) String error,
        @RequestParam(name = "logout", required = false) String logout,
        HttpServletRequest request,
        HttpServletResponse response) {

    ModelAndView mv = new ModelAndView("login");
    HttpSession session= request.getSession(false);
    Authentication auth = SecurityContextHolder.getContext()
        .getAuthentication();
      
    System.out.println("auth ===" + auth);
    System.out.println("logout ===" + logout);
 
    return mv; 
}

This is the output on console:

 

 Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJidW50QGdtYWlsLmNvbSIsImV4cCI6MTU5NjExMjcwM30.fBFMDO--8Q_56LT_qbioiT6p3BOxk3L9OrPVTw5EGbf7oJ0ky7W7PuahIYcdjYSL6-OsHY6qq8tPEetlJO7nEg

auth ===org.springframework.security.authentication.AnonymousAuthenticationToken@823df96a: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS

Please tell me what i am missing here.

Upvotes: 0

Views: 1828

Answers (2)

Midhun Mohan
Midhun Mohan

Reputation: 689

First thing, in the authentication filter token generated and set on the HttpServletResponse header not on the request object's header. Then next the authorization filter checking the request header for token, so there may be the issue of null happened.

Usually authentication and authorization will not be chained like this, but don't know regarding the actual use case you were trying to implement.

Upvotes: 0

matejko219
matejko219

Reputation: 1751

Your JWTAuthenticationFilter that extends UsernamePasswordAuthenticationFilter overrides successfulAuthentication method which by default calls this line:

SecurityContextHolder.getContext().setAuthentication(authResult);

Your implementation do not have this line so after processing of this filter you still do not have Authentication in Spring context. The next filter that is called is your JWTAuthorizationFilter which tries to read header from same request object as in previous filter. JWTAuthenticationFilter sets this header in response object not in request object. So basically you ends up without authentication because if (header == null || !header.startsWith(TOKEN_PREFIX)) is always true after login flow.

Upvotes: 1

Related Questions