SpringBoot--JWT集成配置

一, JWT簡介

    JWT 全名 JSON WEB Token 主要做用爲用戶身份驗證, 普遍應用與先後端分離項目當中.java

    JWT 的優缺點 : https://www.jianshu.com/p/af8360b83a9fweb

二, JWT 在 spring boot 項目當中的配置集成

    1, 引入Maven:spring

<!-- JWT驗證 -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>

   2, application.properties 文件中自定義 校驗 配置數據庫

## JWT
# header:憑證(校驗的變量名), expire:有效期1天(單位:s), secret:祕鑰(普通字符串)
app.jwt.header=token
app.jwt.expire=5184000
app.jwt.secret=aHR0cHM6Ly9teS5vc2NoaW5hLm5ldC91LzM2ODE4Njg=

   3, JWT Bean 文件:apache

package com.gy.fast.common.config.jwt;

import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

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

/**
 * JWT類
 * 
 * @author geYang
 * @date 2018-05-18
 */
@Component
@ConfigurationProperties(prefix = "app.jwt")
public class JWT {
    private Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 加密祕鑰
     */
    private String secret;
    /**
     * 有效時間
     */
    private long expire;
    /**
     * 用戶憑證
     */
    private String header;

    /**
     * 獲取:加密祕鑰
     */
    public String getSecret() {
        return secret;
    }
    
    /**
     * 設置:加密祕鑰
     */
    public void setSecret(String secret) {
        this.secret = secret;
    }
    
    /**
     * 獲取:有效期(s)
     * */
    public long getExpire() {
        return expire;
    }
    /**
     * 設置:有效期(s)
     * */
    public void setExpire(long expire) {
        this.expire = expire;
    }
    
    /**
     * 獲取:憑證
     * */
    public String getHeader() {
        return header;
    }
    /**
     * 設置:憑證
     * */
    public void setHeader(String header) {
        this.header = header;
    }

    /**
     * 生成Token簽名
     * @param userId 用戶ID
     * @return 簽名字符串
     * @author geYang
     * @date 2018-05-18 16:35
     */
    public String generateToken(long userId) {
        System.out.println("header=" + getHeader() + ", expire=" + getExpire() + ", secret=" + getSecret());
        Date nowDate = new Date();
        // 過時時間
        Date expireDate = new Date(nowDate.getTime() + expire * 1000);
        return Jwts.builder().setHeaderParam("typ", "JWT").setSubject(String.valueOf(userId)).setIssuedAt(nowDate)
                .setExpiration(expireDate).signWith(SignatureAlgorithm.HS512, getSecret()).compact();
        // 注意: JDK版本高於1.8, 缺乏 javax.xml.bind.DatatypeConverter jar包,編譯出錯
    }

    /**
     * 獲取簽名信息
     * @param token
     * @author geYang
     * @date 2018-05-18 16:47
     */
    public Claims getClaimByToken(String token) {
        try {
            return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            logger.debug("validate is token error ", e);
            return null;
        }
    }

    /**
     * 判斷Token是否過時
     * @param expiration
     * @return true 過時
     * @author geYang
     * @date 2018-05-18 16:41
     */
    public boolean isTokenExpired(Date expiration) {
        return expiration.before(new Date());
    }

}

     4, 編寫攔截器 JwtInterceptor.java :json

package com.gy.fast.common.config.jwt;

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

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.gy.fast.common.exception.SysException;

import io.jsonwebtoken.Claims;

/**
 * Token驗證攔截器
 * @author geYang
 * @date 2018-05-18
 */
@Component
public class JwtInterceptor  extends HandlerInterceptorAdapter {
    
    @Autowired
    private JWT jwt;
    
    public static final String USER_KEY = "userId";
   
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String servletPath = request.getServletPath();
        System.out.println("ServletPath: " + servletPath);
        // 不須要驗證,直接放行
        boolean isNotCheck = isNotCheck(servletPath);
        if (isNotCheck) {
            return true;
        }
        // 須要驗證
        String token = getToken(request);
        
        if (StringUtils.isBlank(token)) {
            throw new SysException(jwt.getHeader() + "失效,請從新登陸", 401);
        }
        // 獲取簽名信息
        Claims claims = jwt.getClaimByToken(token);
        System.out.println("TOKEN: " + claims);
        // 判斷簽名是否存在或過時
        boolean b = claims==null || claims.isEmpty() || jwt.isTokenExpired(claims.getExpiration());
        if (b) {
            throw new SysException(jwt.getHeader() + "失效,請從新登陸", 401);
        }
        // 將簽名中獲取的用戶信息放入request中;
        request.setAttribute(USER_KEY, claims.getSubject());
        return true;
    }
    
    /**
     * 根據URL判斷當前請求是否不須要校驗, true:須要校驗
     */
    public boolean isNotCheck(String servletPath) {
        // 若 請求接口 以 / 結尾, 則去掉 /
        servletPath = servletPath.endsWith("/")
                ? servletPath.substring(0,servletPath.lastIndexOf("/"))
                : servletPath;
        System.out.println("servletPath = " + servletPath);
        for (String path : NOT_CHECK_URL) {
            System.out.println("path = " + path);
            // path 以 /** 結尾, servletPath 以 path 前綴開頭
            if (path.endsWith("/**")) {
                String pathStart = path.substring(0, path.lastIndexOf("/")+1);
                System.out.println("pathStart = " + pathStart);
                if (servletPath.startsWith(pathStart)) {
                    return true;
                }
                String pathStart2 = path.substring(0, path.lastIndexOf("/"));
                System.out.println("pathStart2 = " + pathStart2);
                if (servletPath.equals(pathStart2)) {
                    return true;
                }
            }
            // servletPath == path
            if (servletPath.equals(path)) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * 獲取請求Token
     */
    private String getToken(HttpServletRequest request) {
    	String token = request.getHeader(jwt.getHeader());
    	if (StringUtils.isBlank(token)) {
    		token = request.getParameter(jwt.getHeader());
    	}
    	return token;
    }
    
    /**
     * 不用攔截的頁面路徑(也可存入數據庫中), 不要以 / 結尾
     */
    private static final String[] NOT_CHECK_URL = {"/test/**", "/login/**"}; 

}

     5,  配置攔截器: 後端

package com.gy.fast.common.config;

import com.gy.fast.common.config.jwt.JwtInterceptor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

/**
 * WebMvc配置
 * @author geYang
 * @date 2018-05-14
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    @Autowired
    private JwtInterceptor jwtInterceptor;
    
   /**
    * APP接口攔截器
    * */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor).addPathPatterns("/app/**");
    }
  

}

    6,  基礎控制器:app

package com.gy.fast.module.work.controller;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gy.fast.common.config.jwt.JwtInterceptor;
import com.gy.fast.common.util.HttpContextUtils;

/**
 * 基礎Controller
 * @author geYang
 * @date 2018-05-15
 */
public abstract class BaseController {
	protected Logger logger = LoggerFactory.getLogger(getClass());

	/**
	 * 獲取當前登陸用戶ID
	 * @author geYang
	 * @date 2018-05-18 19:46
	 */
	protected Long getUserId() {
	    HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
		return Long.parseLong(request.getAttribute(JwtInterceptor.USER_KEY).toString());
	}
}

    7, 編寫用戶控制器:前後端分離

package com.gy.fast.module.work.controller.app;

import java.util.HashMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.gy.fast.common.config.jwt.JWT;
import com.gy.fast.common.util.R;
import com.gy.fast.module.work.controller.BaseController;

/**
 * 用戶
 * @author geYang
 * @date 2018-05-18
 */
@RestController
@RequestMapping("app/user")
public class AppUserController extends BaseController{
    
    @Autowired
    private JWT jwt;
    
    /**
     * 獲取用戶信息
     * @return
     * @author geYang
     * @date 2018-05-18 19:49
     */
    @GetMapping()
    public R info() {
        return R.ok(getUserId());
    }
    
    /**
     * 用戶登陸
     * @return
     * @author geYang
     * @date 2018-05-18 19:55
     */
    @PostMapping("login")
    public R login() {
       //生成token
        String token = jwt.generateToken(10);
        HashMap<String,Object> map = new HashMap<String,Object>();
        map.put("expire", jwt.getExpire());
        map.put("token", token);
        return R.ok(map);
    }
}

    8, 到此處就配置完成了, 經過 postman 測試成功.ide

三, 總結

    在集成 jjwt 時,惟一注意的就是 JDK版本了, JDK版本高於1.8, 在生成token的過程當中因爲base64加密的類: javax.xml.bind.DatatypeConverter 找不到,會產生編譯錯誤.

相關文章
相關標籤/搜索