SpringBoot (9) jwt + interceptor implements token verification

    In the filters and interceptors of the previous two articles, we have mentioned that we can do things such as permission verification. http/https is a stateless protocol. When a user accesses a backend interface, how to judge whether the user has permission? Of course, you can use account + password to verify. However, if you use an account number and password, you need to access the database frequently, obviously, it will bring some additional overhead. This article introduces the use of jwt and interceptors to implement token permission verification.     

    If you are a novice and have not read my previous series of SpringBoot articles, it is recommended to at least read this one:

SpringBoot (4) SpringBoot builds a simple server_springboot makes a service_heart poisoned blog-CSDN blog

    If you want to study systematically from beginning to end, please pay attention to my column and keep updating:

https://blog.csdn.net/qq_21154101/category_12359403.html

Table of contents

​​​​​​​1. The server generates a token

1. Add token dependency

2. Token tools

2. The client carries the token

1. Obtain token when registering

2. Carry the token when requesting

3. Verify token after request

1. Token interceptor

2. Register token interceptor

3. Client request and verification


 

​​​​​​​1. The server generates a token

1. Add token dependency

		<dependency>
			<groupId>com.auth0</groupId>
			<artifactId>java-jwt</artifactId>
			<version>3.4.1</version>
		</dependency>
		<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt</artifactId>
			<version>0.9.1</version>
		</dependency>

2. Token tools

    Write a tool class to generate and verify token

package com.zhaojun.server.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtUtil {
    private static final String secret = "hand2020";
    private static final Long expiration = 1209600L;

    /**
     * 生成用户token,设置token超时时间
     */
    public static String createToken(String name) {
        //过期时间
        Date expireDate = new Date(System.currentTimeMillis() + expiration * 1000);
        Map<String, Object> map = new HashMap<>();
        map.put("alg", "HS256");
        map.put("typ", "JWT");
        String token = JWT.create()
                // 添加头部
                .withHeader(map)
                //可以将基本信息放到claims中
                //userName
                .withClaim("userName", name)
                //超时设置,设置过期的日期
                .withExpiresAt(expireDate)
                //签发时间
                .withIssuedAt(new Date())
                //SECRET加密
                .sign(Algorithm.HMAC256(secret));
        System.out.println(token);
        return token;
    }

    /**
     * 校验token并解析token
     */
    public static boolean verifyToken(String token) {
        DecodedJWT jwt;
        try {
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret)).build();
            jwt = verifier.verify(token);
            if (jwt.getExpiresAt().before(new Date())) {
                System.out.println("token过期");
                return false;
            }
        } catch (Exception e) {
            //解码异常则抛出异常
            System.out.println("token解析异常:" + e.getMessage());
            return false;
        }
        return true;
    }

}

2. The client carries the token

    Because the general token is placed in the header, it is not possible to add the header when accessing directly in the browser. Of course, tools such as postman can be used. I work on Android myself, and I quickly write several requests and simple pages directly based on okhttp, including the previous register and login. The logic to be implemented is as follows:

  • When registering a new user, a token is generated to tell the client, and the client saves the token locally after receiving the token
  • All subsequent requests from the client carry the token in the header
  • After the server receives the request, it first checks the token, and if the token is legal, it is released

1. Obtain token when registering

    First of all, I first register an account, the account is test, the password is 123456, and the mobile phone number is 123456. In the sample code below, I wrote a registration request using okhttp.

    private void doRegister() {
        String name = mNameEditText.getText().toString();
        String password = mPasswordEditText.getText().toString();
        String phone = mPhoneEditText.getText().toString();
        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(password) || TextUtils.isEmpty(phone)) {
            Toast.makeText(this, "用户名、手机号或密码为空", Toast.LENGTH_SHORT).show();
        }
        String url = URL + "name=" + name + "&password=" + password + "&phone=" + phone;
        new Thread(new Runnable() {
            @Override
            public void run() {
                OkHttpClient client = new OkHttpClient();
                Request request = new Request.Builder()
                        .url(url)
                        .build();
                try {
                    Response response = client.newCall(request).execute();
                    ResponseBody body = response.body();
                    if (body != null) {
                        String result = body.string();
                        Log.d("TTTT",result);
                        Gson gson = new Gson();
                        TypeToken<RegisterResult> typeToken = new TypeToken<RegisterResult>() {
                        };
                        RegisterResult registerResult = gson.fromJson(result, typeToken);
                        if (registerResult != null) {
                            String token = registerResult.getToken();
                            SPUtils.getInstance().put("token", token);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    After the registration is successful, you can see that the backend returns the following json:

2. Carry the token when requesting

    The following sample code, I use okhttp to write a header with a token request, this token was returned and saved locally during the previous registration.

    private void tokenLogin() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                OkHttpClient client = new OkHttpClient();
                Request request = new Request.Builder()
                        .url(url)
                        .addHeader("token", SPUtils.getInstance().getString("token"))
                        .build();
                try {
                    Response response = client.newCall(request).execute();
                    ResponseBody body = response.body();
                    if (body != null) {
                        String result = body.string();
                        Log.d("TTTT", result);
                        Gson gson = new Gson();
                        TypeToken<LoginResult> typeToken = new TypeToken<LoginResult>() {
                        };
                        LoginResult loginResult = gson.fromJson(result, typeToken);
                        if (loginResult != null) {
                            int code = loginResult.getCode();
                            if (code != 0) {
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        startLoginActivity();
                                    }
                                });
                            }
                        }
                    }
                } catch (Exception e) {
                    Log.d("TTTT",e.getMessage());
                }
            }
        }).start();
    }

3. Verify token after request

    Everything is ready, the generation and request carry tokens are done. Next, you need to implement a token interceptor on the backend to intercept any requests other than the registration interface. If the token verification passes, it will be released directly, otherwise it will be intercepted and an error message will be returned. Two types are defined here: token is empty and token is invalid.

1. Token interceptor

package com.zhaojun.server.interceptor;

import com.zhaojun.server.util.JwtUtil;
import com.zhaojun.server.util.TextUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class TokenInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpServletResponseWrapper wrapper = new HttpServletResponseWrapper(response);
        String token = request.getHeader("token");
        if (TextUtils.isEmpty(token)) {
            wrapper.sendRedirect("fail/token/null");
            System.out.println("null token");
            return false;
        }
        if (!JwtUtil.verifyToken(token)) {
            wrapper.sendRedirect("fail/token/invalid");
            System.out.println("invalid token");
            return false;
        }
        System.out.println("valid token");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("拦截器处理结束...");
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("请求结束...");
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

2. Register token interceptor

    It is not recommended to directly intercept all urls, just intercept the required requests.

package com.zhaojun.server.interceptor;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TokenInterceptor())
                .addPathPatterns("/login")
                .excludePathPatterns("/register");
    }
}

3. Client request and verification

After the client initiates a request with the token in the header, you can see that the verification is successful:

After using the browser to make a request without token, redirect:

Guess you like

Origin blog.csdn.net/qq_21154101/article/details/131820238