In the previous section, we used a custom authentication token class to support multiple logins, but still session-based. In this section, we need to combine JWT to do the authentication function.

demand

When you log in, return the token. Subsequent interfaces use tokens for access.

  1. After successful login, return token.
  2. Verify the token when sending the request.

Solution Analysis

Before, we were all session-based. So I need to close the Session first.

Then, in the login success handler, the token is generated and returned.

When sending a business request, an interceptor is used to verify that the token is valid. If it is valid, set it to SecurityContextHoler.

implementation

Token returned after successful login

  1. The introduction of JWT package

    JDK 11 removed some of the required classes from the JWT package. Let me reintroduce it here.

            <! -- to solve all of the following are Jwt tools in Java. Lang. ClassNotFoundException: javax.mail. XML. Bind. DatatypeConverter - >
            <dependency>
                <groupId>javax.xml.bind</groupId>
                <artifactId>jaxb-api</artifactId>
                <version>2.3.0</version>
            </dependency>
            <dependency>
                <groupId>com.sun.xml.bind</groupId>
                <artifactId>jaxb-impl</artifactId>
                <version>2.3.0</version>
            </dependency>
            <dependency>
                <groupId>com.sun.xml.bind</groupId>
                <artifactId>jaxb-core</artifactId>
                <version>2.3.0</version>
            </dependency>
            <dependency>
                <groupId>javax.activation</groupId>
                <artifactId>activation</artifactId>
                <version>1.1.1</version>
            </dependency>
            <! - above all to solve the Java. Lang. ClassNotFoundException: javax.mail. XML. Bind. DatatypeConverter - >
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>0.9.1</version>
            </dependency>
    Copy the code

    Newer packages can also be introduced in the following ways, and there are some apis that are different from older versions that need to be noted.

    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.11.2</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.11.2</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId> <! -- or jjwt-gson if Gson is preferred -->
        <version>0.11.2</version>
        <scope>runtime</scope>
    </dependency>
    Copy the code
  2. Modified the method for handling authentication success

    filter.setAuthenticationSuccessHandler((request, response, authentication) -> {
                response.setContentType("application/json; charset=utf-8");
                PrintWriter out = response.getWriter();
                MyUserDetails userDetails = (MyUserDetails) authentication.getPrincipal();
                userDetails.setPassword(null);
     
                String jwt = Jwts.builder()
                        .claim("userId", userDetails.getId()) // User role
                        .setSubject(userDetails.getUsername()) / / theme
                        // Expiration time
                        .setExpiration(new Date(System.currentTimeMillis() + 10 * 60 * 1000))
                        .signWith(SignatureAlgorithm.HS512, "damai") // Encryption algorithm key
                        .compact();
                // userDetails Adds the token field
                userDetails.setToken(jwt);
                HashMap<String, Object> result = new HashMap<>();
                result.put("code"."000000");
                result.put("msg"."Successful landing.");
                result.put("data",userDetails);
                String s = new ObjectMapper().writeValueAsString(result);
                out.write(s);
                out.flush();
                out.close();
            });
    Copy the code

Send a service request to verify the token

  1. Adding filters

    @Component
    @Slf4j
    public class JwtTokenFilter extends OncePerRequestFilter {
     
        @Autowired
        MyUserDetailServiceImpl myUserDetailService;
     
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            // Spring Security will throw an exception if the token is not authenticated
            String token = request.getHeader(JwtUtil.TOKEN_HEADER);
            if (StringUtils.isBlank(token)) {
                filterChain.doFilter(request,response);
                return;
            }
            log.info("jwtToken:{}",token);
            / / token
            Claims claims = Jwts.parser().setSigningKey(JwtUtil.SECRET).parseClaimsJws(token.replace(JwtUtil.TOKEN_HEAD, "")).getBody();
            String username = claims.getSubject();
            // Load user information
            UserDetails userDetails = myUserDetailService.loadUserByUsername(username);
            MyAuthenticationToken myAuthenticationToken = new MyAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            // Set to the Security contextSecurityContextHolder.getContext().setAuthentication(myAuthenticationToken); filterChain.doFilter(request,response); }}Copy the code
  2. Modifying the Security Configuration

     @Override
        protected void configure(HttpSecurity http) throws Exception {
     
           // Custom exception handling when not logged in
           http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
                response.setContentType("application/json; charset=utf-8");
                PrintWriter out = response.getWriter();
                HashMap<String, Object> result = new HashMap<>();
                result.put("code"."111112");
                result.put("msg",authException.getMessage());
                out.write(new ObjectMapper().writeValueAsString(result));
                out.flush();
                out.close();
            });
     
          http.authorizeRequests()
                    .anyRequest()
                    .authenticated()
                    .and()
                    .csrf()
                    .disable();
            / / close the session
            http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
            / / add JWT filters, attention must be before UsernamePasswordAuthenticationFilter
            http.addFilterBefore(jwtTokenFilter,UsernamePasswordAuthenticationFilter.class);
            http.addFilterAt(myAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
        }
    Copy the code

landing

  1. Login return Token

    {
        "msg": "Successful landing."."code": "000000"."data": {
            "id": 1."phone": null."username": "Zhang"And..."token": "eyJhbGciOiJIUzUxMiJ9.eyJ1c2VySWQiOjEsInN1YiI6IuW8oOS4iSIsImV4cCI6MTYwNTQ5OTk0Nn0.ZlPGSpyOLaKSeGSdDhuyuLDASJsya1rP2cVdy- JBVKVIBijC6DmOlNRnQzhvFcofWFWqeyhHpGsio1g6HmBHYw"."authorities": [{"authority": "admin"}].}}Copy the code
  2. Direct access to the business interface

    The request header does not carry the token.

    {
        "msg": "Full authentication is required to access this resource"."code": "111112"
    }
    Copy the code
  3. Carries a token and requests a service interface

    {
        "_links": {
            "self": {
                "href": "http://localhost:3344/actuator"."templated": false
            },
            "health": {
                "href": "http://localhost:3344/actuator/health"."templated": false
            },
            "health-path": {
                "href": "http://localhost:3344/actuator/health/{*path}"."templated": true
            },
            "info": {
                "href": "http://localhost:3344/actuator/info"."templated": false}}}Copy the code