Java - token storage and acquisition

1. Tool class for obtaining token

Q: Why write tool classes? ? ?
Answer: Because we don't know how the front-end stores the token, we can get the token by calling the Token tool class. The Token tool class will check the header, the attribute value in the URL, and the cookie, etc.! ! !

public class UserTokenUtil {

    public static String getToken(HttpServletRequest request, String tokenName) {
        String token = null;

        // 1. header
        token = request.getHeader(tokenName);
        if (StringUtils.isNotBlank(token)) {
            return token;
        }
        // 2. cookie
        Cookie[] cookies = request.getCookies();
        if (cookies != null && cookies.length != 0) {
            for (Cookie cookie : cookies) {
                if (cookie != null && tokenName.equals(cookie.getName())) {
                    token = cookie.getValue();
                    break;
                }
            }
        }
        if (StringUtils.isNotBlank(token)) {
            return token;
        }
        // 3. parameter
        token = request.getParameter(tokenName);
        return token;
    }
    
}

2. The header stores the token

2.1 Front-end storage token

The first step to install: js-cookie

npm install --save js-cookie

The second step is to introduce: (here we take vue development as an example, and introduce it in main.js!)

// 全局应用Cookie
import Cookie from 'js-cookie'
Vue.prototype.$Cookie = Cookie

Step 3: The front end accesses the back end and stores the token after it is obtained.

// 向后端发送登录请求
this.axios({
  method: "post",
  headers: {
    // 测试header保存数据
    "hahah": "text_header_save_Data"
  },
  url: "http://" + that.$store.state.host + "/zm-task/login/goLogin",
  data: {
    "loginNumber": that.ruleForm.user,
    "password": that.ruleForm.pass
  }
}).then(function (res) {
  // console.log(res.data);
  //根据后端返回的状态查看账号是否正确
  if (res.data.code == 0) {
    // ===============  cookie的操作 ================
    // 博文: https://blog.csdn.net/qq_16828495/article/details/120783389

    // 保存token
    that.$Cookie.set("token", res.data.data.token);
    //登录成功后跳转到后台的个人信息界面
    that.$message.success("登录成功!");
    that.$router.push({path: "/userInfo"});
  } else {
    that.$message.error(res.data.message);
  }
})
.catch(err => {
  // 后端如果出现异常,前端会报401错误。 这里捕获401错误。
  //console.log(err.response.data)
  if (err.response != null) {
    this.$message.error(err.response.data.message);
  } else {
    this.$message.error("未知异常");
  }
})

2.2 Access carrying token

axios({
  method: "get",
  headers: {
    // 传输token用于验证当前登录用户
    "token": that.$Cookie.get("token"),
  },
  url: "http://" + that.$store.state.host + "/zm-task/login/updateEmail",
  params:{
    // 需要修改的邮箱属性
    "email": formName.email
  }
}).then(res => {
  // 直接显示后端传来的信息
  that.$message.success(res.data.message);
}).catch(err => {
  // 后端如果出现异常,前端会报401错误。 这里捕获401错误。
  if (err.response != null) {
    this.$message.error(err.response.data.message);
  } else {
    this.$message.error("未知异常");
  }
})

2.3 The backend obtains the token and verifies it (verifies in the interceptor)

@Slf4j
public class UserLoginAuthInterceptor implements HandlerInterceptor {

    /**
     *
     * @param request : 请求(通过请求获取token登陆凭证)
     * @param response : 返回给前端的响应
     * @param handler : 该参数中包含了对应方法的信息。比如:方法中的参数类型、参数的注解、方法的注解等信息。
     * @return
     * @throws Exception : 向上抛出异常
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 排除资源请求
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        // 通过handler获取方法上面的Login注解
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Login login = handlerMethod.getMethod().getAnnotation(Login.class);
        //说明方法上面没有Login注解
        if (login == null){
            return true;
        }
        // 有Login注解,但是Login的属性值为false
        if (Boolean.FALSE.equals(login.loginRequired())){
            return true;
        }
        // 获取请求头中的token请求
        String token = request.getHeader("token");
        // 根据token获取登陆的用户信息(登陆账号, 登陆ip, 用户权限0、1、2)
        UserLoginModel userLoginModel = JWTUtil.tokenToUser(token, request);
        // 获取当前请求的ip地址!!!  (公网才有效果)
        String ipAddr = IPUtil.getIpAddr(request);
        if (StringUtils.isBlank(ipAddr)){
            throw new BizException(ServerEnum.IP_GET_ERROR);
        }
        if (userLoginModel == null){

            //todo redis保存ip地址,防止被爆刷接口

            throw new BizException(ServerEnum.LOGIN_ERROR);
        }
        // 这里只有线上测试才能有效果,非线上测试无效果! (ipAddr公网才能获取准确的IPv4)
        if (!ipAddr.equals(userLoginModel.getIP())){
            // 登陆IP 与 请求IP不相同,所以重新进行登陆
            log.error("登陆ip:{}, 请求ip:{}", userLoginModel.getIP(), ipAddr);

            //todo redis保存ip地址,防止被爆刷接口

            throw new BizException(ServerEnum.LOGIN_ERROR);
        }
        //使用ThreadLocal保存用户信息
        UserLoginInfoThreadLocalUtil.set(userLoginModel);
        return true;
    }

    /**
     * 在整个请求处理完毕后进行回调,也就是说视图渲染完毕或者调用方已经拿到响应。
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //防止内存泄漏
        UserLoginInfoThreadLocalUtil.clear();
    }
}

3. Attribute values ​​in the URL

Take this address as an example: https://editor.csdn.net/md?articleId=125379150&name=Thomas
When making a Get or Post request, just add parameters after the url! ! !

public class UserTokenUtil {

    public static String getToken(HttpServletRequest request, String tokenName) {
        String articleId = null;
        String name = null;
        // articleId = 125379150
        articleId = request.getParameter(articleId);
        // name = Thomas
        name = request.getParameter(name);
        return articleId + " " + name;
    }
    
}

4. Cookie

4.1 Controller code

@RestController
@RequestMapping("/cookie")
public class CookieTestController {

    // http://localhost:8080/cookie/login/tokentoken?id=13213
    @GetMapping("/login/{token}")
    public void getLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //设置请求编码格式
        request.setCharacterEncoding("UTF-8");
        //设置响应编码格式
        response.setCharacterEncoding("UTF-8");
        //设置数据返回的类型
        response.setContentType("text/json");
        // 获取请求信息(参数id对应的值)
        Integer id = Integer.valueOf(request.getParameter("id"));
        // 原本想获取请求url中的值,但是无法通过request获取!!!!
        String token = request.getPathInfo();

        System.out.println(request.getMethod()); // GET
        System.out.println(request.getRequestURI()); // /cookie/login/tokentoken
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()){
            // id (说明只能获取路径?后面的参数,不能获取包含在路径中的参数!!!)
            System.out.println(parameterNames.nextElement());
        }
        // 添加cookie信息
        Cookie c1 = new Cookie("token_cxp_zm", "chsdiufvbndsufyvbnduh");
        Cookie c2 = new Cookie("cxp-love_zm", "hahahahaha---cxp---love=---zm");
        response.addCookie(c1);
        response.addCookie(c2);

        PrintWriter out = response.getWriter();
        out.write("XXXXXXX" + id + " == " + token);
        out.flush();//刷新该流的缓冲
        out.close();//关闭流
    }

    // http://localhost:8080/cookie/login/getCookie
    @GetMapping("/login/getCookie")
    public void getCookies(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //设置数据返回的类型
        response.setContentType("text/json");
        //设置请求编码格式
        request.setCharacterEncoding("UTF-8");
        //设置响应编码格式
        response.setCharacterEncoding("UTF-8");
        String State = "fail";
        //获取Cookie信息数组
        Cookie[] cks = request.getCookies();
        if (cks != null){
            for(Cookie cookie : cks){
                System.out.println(cookie.getName() + "=" + cookie.getValue());
            }
        }
        PrintWriter out=response.getWriter();
        out.write("测试获取Cookie");
        out.flush();//刷新该流的缓冲
        out.close();//关闭流
    }

}

4.2 Testing

Insert key-value value into Cookie! ! !

Subsequent Get or Post requests do not require us to actively insert cookies, and the requests will automatically carry cookies for access! ! !

The explanation is over! ! !

Note:
Problem: Because the backend sets cookies, it will involve cross-domain, but cookies cannot cross-domain. This causes the token in the cookie to not be included in the request header when the page is redirected. At this time, as long as the Domain is set to .+ first-level domain name, the problem of cross-domain cookies can be solved.

The use of Token (two ways):
1. Directly return the token character, let the front-end students store it (Cookie, localStorage, SessionStorage), and manually carry the token when requesting! !
2. When back-end students set cookies, just pay attention to cross-domain issues!

Guess you like

Origin blog.csdn.net/jcc4261/article/details/129611486