Arul Rozario
Arul Rozario

Reputation: 759

How to implement JWT based authentication and authorization in Spring Security

How to implement JWT based authentication and authorization in Spring Security

I am trying to implement jwt based authentication and authorization in my spring boot app. I followed a tutorial written here. But it does not do anything in my app. It does not return jwt token rather I am authenticated and my request is fulfilled. I am new to spring security. here is my code.

I want my app return jwt token and using the token the requests must be authorized.

Here is my code.

JWTAuthenticationFilter.java

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;

    @Autowired
    CustomUserDetailsService userService;

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

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
        throws AuthenticationException {

    try {

        CustomUserDetails user = new ObjectMapper().readValue(request.getInputStream(), CustomUserDetails.class);

        return authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), new ArrayList<>()));
    } catch (Exception e) {
    }

    return super.attemptAuthentication(request, response);
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
        Authentication auth) {

    String loggedInUser = ((CustomUserDetails) auth.getPrincipal()).getUsername();

    Claims claims = Jwts.claims().setSubject(loggedInUser);

    if (loggedInUser != null) {
        CustomUserDetails user = (CustomUserDetails) userService.loadUserByUsername(loggedInUser);
        String roles[] = {};

        for (Role role : user.getUser().getUserRoles()) {
        roles[roles.length + 1] = role.getRole();
        }
        claims.put("roles", roles);

        claims.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME));

    }

    String token = Jwts.builder().setClaims(claims)
        .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
        .signWith(SignatureAlgorithm.HS512, SECRET.getBytes()).compact();
    response.addHeader(HEADER_STRING, TOKEN_PREFIX + token);

    }

}

JWTAuthorizationFilter.java

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;

public class JWTAuthorizationFilter extends BasicAuthenticationFilter {

    public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {
    super(authenticationManager);

    }

    @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 authentication = getToken(request);
    SecurityContextHolder.getContext().setAuthentication(authentication);
    chain.doFilter(request, response);

    }

    @SuppressWarnings("unchecked")
    private UsernamePasswordAuthenticationToken getToken(HttpServletRequest request) {

    String token = request.getHeader(HEADER_STRING);

    System.out.println("-----------------------------------------------------");
    System.out.println("Token: " + token);
    System.out.println("-----------------------------------------------------");

    if (token != null) {

        Claims claims = Jwts.parser().setSigningKey(SECRET.getBytes())
            .parseClaimsJws(token.replace(TOKEN_PREFIX, "")).getBody();
        String user = claims.getSubject();

        ArrayList<String> roles = (ArrayList<String>) claims.get("roles");

        ArrayList<MyGrantedAuthority> rolesList = new ArrayList<>();

        if (roles != null) {
        for (String role : roles) {
            rolesList.add(new MyGrantedAuthority(role));
        }
        }
        if (user != null) {
        return new UsernamePasswordAuthenticationToken(user, null, null);
        }
        return null;
    }
    return null;
    }

}

SecurityConfig.java

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

    @Qualifier("userDetailsService")
    @Autowired
    CustomUserDetailsService userDetailsService;

    @Autowired
    PasswordEncoder passwordEncoder;

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    JWTAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
    try {
        auth.userDetailsService(this.userDetailsService).passwordEncoder(passwordEncoder);

    } catch (Exception e) {

    }

    }

    /*
     * @Autowired public void configureGlobal(AuthenticationManagerBuilder auth)
     * throws Exception {
     * auth.inMemoryAuthentication().withUser("student").password("student").roles(
     * "student").and().withUser("admin") .password("admin").roles("admin"); }
     */

    @Override
    protected void configure(HttpSecurity http) throws Exception {

    http.csrf().disable();
    // http.authorizeRequests().anyRequest().permitAll();

    // http.authorizeRequests().antMatchers("/api/**").permitAll();

    http.addFilter(new JWTAuthenticationFilter(authenticationManager));
    http.addFilter(new JWTAuthorizationFilter(authenticationManager));

    http.authorizeRequests().antMatchers("/api/student/**").hasAnyRole("STUDENT", "ADMIN");
    http.authorizeRequests().antMatchers("/api/admin/**").hasRole("ADMIN");
    http.authorizeRequests().antMatchers("/api/libararian/**").hasAnyRole("LIBRARIAN", "ADMIN");
    http.authorizeRequests().antMatchers("/api/staff/**").hasAnyRole("STAFF", "ADMIN");
    http.authorizeRequests().antMatchers("/api/teacher/**").hasAnyRole("TEACHER", "ADMIN");
    http.authorizeRequests().antMatchers("/api/parent/**").hasAnyRole("PARENT", "ADMIN");
    http.httpBasic().authenticationEntryPoint(jwtAuthenticationEntryPoint);

    http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    // http.formLogin().and().logout().logoutSuccessUrl("/login?logout").permitAll();

    }

}

MyGrantedAuthority.java

    public class MyGrantedAuthority implements GrantedAuthority {

        String authority;

        MyGrantedAuthority(String authority) {
        this.authority = authority;
        }

        @Override
        public String getAuthority() {
        // TODO Auto-generated method stub
        return authority;
        }

    }

JWTAuthenticationEntryPoint.java

@Component
public class JWTAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
        throws IOException, ServletException {
    response.setStatus(403);
    response.setContentType(MediaType.APPLICATION_JSON_VALUE);

    String message;
    if (exception.getCause() != null) {
        message = exception.getCause().getMessage();
    } else {
        message = exception.getMessage();
    }
    byte[] body = new ObjectMapper().writeValueAsBytes(Collections.singletonMap("error", message));
    response.getOutputStream().write(body);
    }

}

Upvotes: 4

Views: 15034

Answers (2)

Arul Rozario
Arul Rozario

Reputation: 759

I got it. I followed another tutorial which made my job easy. Here is the complete rewrite working code

TokenProvider.java

package com.cloudsofts.cloudschool.security;

import static com.cloudsofts.cloudschool.security.SecurityConstants.EXPIRATION_TIME;
import static com.cloudsofts.cloudschool.security.SecurityConstants.SECRET;

import java.util.ArrayList;
import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import com.cloudsofts.cloudschool.people.users.pojos.CustomUserDetails;
import com.cloudsofts.cloudschool.people.users.pojos.Role;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

@Component
public class TokenProvider {

    @Autowired
    CustomUserDetailsService userService;

    public String createToken(String username) {

    CustomUserDetails user = (CustomUserDetails) userService.loadUserByUsername(username);

    Claims claims = Jwts.claims().setSubject(username);

    ArrayList<String> rolesList = new ArrayList<String>();

    for (Role role : user.getUser().getUserRoles()) {
        rolesList.add(role.getRole());

    }

    claims.put("roles", rolesList);

    String token = Jwts.builder().setClaims(claims)
        .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)).setIssuedAt(new Date())
        .signWith(SignatureAlgorithm.HS512, SECRET).compact();

    return token;
    }

    public Authentication getAuthentication(String token) {
    String username = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody().getSubject();
    UserDetails userDetails = this.userService.loadUserByUsername(username);

    return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
    }
}

JWTFilter.java

package com.cloudsofts.cloudschool.security;

import static com.cloudsofts.cloudschool.security.SecurityConstants.HEADER_STRING;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;

import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;

public class JWTFilter extends GenericFilterBean {

    public final static String AUTHORIZATION_HEADER = "Authorization";

    private final TokenProvider tokenProvider;

    public JWTFilter(TokenProvider tokenProvider) {
    this.tokenProvider = tokenProvider;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
        throws IOException, ServletException {
    try {
        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        String jwt = resolveToken(httpRequest);
        if (jwt != null) {
        Authentication authentication = this.tokenProvider.getAuthentication(jwt);
        if (authentication != null) {
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        }

        chain.doFilter(servletRequest, servletResponse);
    } catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | SignatureException
        | UsernameNotFoundException e) {
        // Application.logger.info("Security exception {}", e.getMessage());
        ((HttpServletResponse) servletResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    }
    }

    private String resolveToken(HttpServletRequest request) {
    String bearerToken = request.getHeader(HEADER_STRING);
    if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
        return bearerToken.substring(7, bearerToken.length());
    }
    return null;
    }

}

JWTConfigurer.java

import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

public class JWTConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

    private final TokenProvider tokenProvider;

    public JWTConfigurer(TokenProvider tokenProvider) {
    this.tokenProvider = tokenProvider;
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {

    JWTFilter customFilter = new JWTFilter(tokenProvider);
    http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
    }

}

SecurityConfig.java

import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.cloudsofts.cloudschool.people.users.pojos.User;

@RestController
public class LoginController {

    private AuthenticationManager authenticationManager;

    private TokenProvider tokenProvider;

    private CustomUserDetailsService userService;

    LoginController(AuthenticationManager auth, CustomUserDetailsService service, TokenProvider tokenProvider) {
    this.authenticationManager = auth;
    this.userService = service;
    this.tokenProvider = tokenProvider;
    }

    @PostMapping("/login")
    public String getToken(@RequestBody User user, HttpServletResponse response) {

    UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(user.getUsername(),
        user.getPassword());

    authenticationManager.authenticate(authToken);
    return tokenProvider.createToken(user.getUsername());

    }
}

LoginController.java

import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.cloudsofts.cloudschool.people.users.pojos.User;

@RestController
public class LoginController {

    private AuthenticationManager authenticationManager;

    private TokenProvider tokenProvider;

    private CustomUserDetailsService userService;

    LoginController(AuthenticationManager auth, CustomUserDetailsService service, TokenProvider tokenProvider) {
    this.authenticationManager = auth;
    this.userService = service;
    this.tokenProvider = tokenProvider;
    }

    @PostMapping("/login")
    public String getToken(@RequestBody User user, HttpServletResponse response) {

    UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(user.getUsername(),
        user.getPassword());

    authenticationManager.authenticate(authToken);
    return tokenProvider.createToken(user.getUsername());

    }
}

Link to the Github project that helped me.

Upvotes: 5

Filipe Tabosa
Filipe Tabosa

Reputation: 303

I am also using jwt authentication on my project and I could see that you are missing an entry point which should be used on the project. I will tell you how I implemented it and see if it can help you =). You need to implement an authenticationEntryPoint in order to tell the code how the authentication will be done. It can be added after the filters, on the http.authorizerequest, with the command:

    .authenticationEntryPoint(jwtAuthEndPoint);

where jwtAuthEndPoint is the following component:

  @Component
  public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
  @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
        AuthenticationException e) throws IOException, ServletException {
    httpServletResponse.setStatus(SC_FORBIDDEN);
    httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);

    String message;
    if (e.getCause() != null) {
        message = e.getCause().getMessage();
    } else {
        message = e.getMessage();
    }
    byte[] body = new ObjectMapper().writeValueAsBytes(Collections.singletonMap("error", message));
    httpServletResponse.getOutputStream().write(body);
    }
}

I would also suggest you to take a look on this tutorial, which helped me A LOT in this case: https://sdqali.in/blog/2016/07/07/jwt-authentication-with-spring-web---part-4/

Upvotes: 2

Related Questions