springmvc結合jwt的使用,實現前後端分離token鑑定

1.首先需要導入maven依賴

		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-core</artifactId>
			<version>2.9.4</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.9.4</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-annotations</artifactId>
			<version>2.9.4</version>
		</dependency>
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.41</version>
		</dependency>
		<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt</artifactId>
			<version>0.7.0</version>
		</dependency>
		<dependency>
			<groupId>com.auth0</groupId>
			<artifactId>java-jwt</artifactId>
			<version>3.2.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>5.1.3.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>5.1.3.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz</artifactId>
			<version>2.3.0</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>5.1.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>5.1.3.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>5.1.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>5.1.3.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.16</version>
		</dependency>

2.編寫jwt工具類

package com.dqw.util;

import java.security.Key;
import java.util.Date;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
/**
 * jwt工具類
 * @ClassName:  JWTUtil   
 * @Description:TODO
 * @author: 丁乾文
 * @date:   2019年5月13日 下午7:48:21
 */
public class JWTUtil {
	/**
	 * token加密時使用的密鑰
	 * 一旦得到該密鑰也就可以僞造token了
	 */
	public static String sercetKey = "InMySchoolOnline";
	/**
	 * 代表token的有效時間
	 */
	public final static long keeptime = 1800000;
	
	/**
	 * JWT由3個部分組成,分別是 頭部Header,載荷Payload一般是用戶信息和聲明,簽證Signature一般是密鑰和簽名
	 * 當頭部用base64進行編碼後一般都會呈現eyJ...形式,而載荷爲非強制使用,簽證則包含了哈希算法加密後的數據,包括轉碼後的header,payload和sercetKey
	 * 而payload又包含幾個部分,issuer簽發者,subject面向用戶,iat簽發時間,exp過期時間,aud接收方。
	 * @Title: generToken   
	 * @Description: TODO
	 * @param: @param id 用戶id
	 * @param: @param issuer 簽發者
	 * @param: @param subject 一般用戶名 
	 * @param: @return      
	 * @return: String      
	 * @throws
	 */
	public static String generToken(String id, String issuer, String subject) {
		long ttlMillis = keeptime;
		SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
		//使用Hash256算法進行加密
		long nowMillis = System.currentTimeMillis();
		Date now = new Date(nowMillis);
		//獲取系統時間以便設置token有效時間
		byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(sercetKey);
		//將密鑰轉碼爲base64形式,再轉爲字節碼
		Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
		//對其使用Hash256進行加密
		JwtBuilder builder = Jwts.builder().setId(id).setIssuedAt(now);
		//JWT生成類,此時設置iat,以及根據傳入的id設置token
		if (subject != null) {
			builder.setSubject(subject);
		}
		if (issuer != null) {
			builder.setIssuer(issuer);
		}
		//由於Payload是非必須加入的,所以這時候要加入檢測
		builder.signWith(signatureAlgorithm, signingKey);
		//進行簽名,生成Signature
		if (ttlMillis >= 0) {
			long expMillis = nowMillis + ttlMillis;
			Date exp = new Date(expMillis);
			builder.setExpiration(exp);
		}
		//返回最終的token結果
		return builder.compact();
	}
	/**
	 * 該函數用於更新token
	 * @Title: updateToken   
	 * @Description: TODO
	 * @param: @param token
	 * @param: @return      
	 * @return: String      
	 * @throws
	 */
	public static String updateToken(String token) {
		//Claims就是包含了我們的Payload信息類
		Claims claims = verifyToken(token);
		String id = claims.getId();
		String subject = claims.getSubject();
		String issuer = claims.getIssuer();
		//生成新的token,根據現在的時間
		return generToken(id, issuer, subject);
	}
	/**
	 * 將token解密出來,將payload信息包裝成Claims類返回
	 * @Title: verifyToken   
	 * @Description: TODO
	 * @param: @param token
	 * @param: @return      
	 * @return: Claims      
	 * @throws
	 */
	private static Claims verifyToken(String token) {
		Claims claims = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(sercetKey))
				.parseClaimsJws(token).getBody();
		return claims;
	}
}

3.編輯token鑑定spring攔截器

package com.dqw.interceptor;

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

import org.apache.log4j.Logger;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.alibaba.fastjson.JSON;
import com.dqw.util.JWTUtil;
import com.dqw.util.ResponseData;
/**
 * token鑑定
 * @ClassName:  HeaderTokenInterceptor   
 * @Description:TODO
 * @author: 丁乾文
 * @date:   2019年5月13日 下午8:52:40
 */
public class HeaderTokenInterceptor implements HandlerInterceptor {
	private static final Logger LOG = Logger.getLogger(HeaderTokenInterceptor.class);
	public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
			Object handler) throws Exception {
		ResponseData responseData = null;
		// 獲取我們請求頭中的token驗證字符
		String headerToken = httpServletRequest.getHeader("token");
		// 檢測當前頁面,我們設置當頁面不是登錄頁面時對其進行攔截
		// 具體方法就是檢測URL中有沒有login字符串
		if (!httpServletRequest.getRequestURI().contains("login")) {
			if (headerToken == null) {
				// 如果token不存在的話,返回錯誤信息。
				responseData=ResponseData.customerError();
			}
			try {
				// 對token進行更新與驗證
				headerToken = JWTUtil.updateToken(headerToken);
				LOG.debug("token驗證通過,並續期了");
			} catch (Exception e) {
				LOG.debug("token驗證出現異常!");
				// 當token驗證出現異常返回錯誤信息,token不匹配
				responseData=ResponseData.customerError();
			}
		}
		if(responseData!=null) {//如果有錯誤信息
			httpServletResponse.getWriter().write(JSON.toJSONString(responseData));
			return false;
		}else {
			// 將token加入返回頁面的header
			httpServletResponse.setHeader("token", headerToken);
			return true;
		}
	}
	public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o,
			ModelAndView modelAndView) throws Exception {
	}
	public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
			Object o, Exception e) throws Exception {
	}
}

4.項目中用到的其他類

package com.dqw.util;

import java.util.HashMap;
import java.util.Map;
/**
 * 響應實體
 * @ClassName:  ResponseData   
 * @Description:TODO
 * @author: 丁乾文
 * @date:   2019年5月13日 下午8:53:10
 */
public class ResponseData {
	private final String message;
    private final int code;
    private final Map<String, Object> data = new HashMap<String, Object>();

    public String getMessage() {
        return message;
    }

    public int getCode() {
        return code;
    }

    public Map<String, Object> getData() {
        return data;
    }

    public ResponseData putDataValue(String key, Object value) {
        data.put(key, value);
        return this;
    }

    private ResponseData(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public static ResponseData ok() {
        return new ResponseData(200, "Ok");
    }

    public static ResponseData notFound() {
        return new ResponseData(404, "Not Found");
    }

    public static ResponseData badRequest() {
        return new ResponseData(400, "Bad Request");
    }

    public static ResponseData forbidden() {
        return new ResponseData(403, "Forbidden");
    }

    public static ResponseData unauthorized() {
        return new ResponseData(401, "unauthorized");
    }

    public static ResponseData serverInternalError() {
        return new ResponseData(500, "Server Internal Error");
    }

    public static ResponseData customerError() {
        return new ResponseData(1001, "customer Error");
    }
}



package com.dqw.po;
/**
 * 用戶實體類
 * @ClassName:  User   
 * @Description:TODO
 * @author: 丁乾文
 * @date:   2019年5月13日 下午8:43:38
 */
public class User {
	private Integer id;
	private String email;
	private String password;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}

package com.dqw.interceptor;

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

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
 * 主要解決請求跨域問題
 * @ClassName:  HttpInterceptor   
 * @Description:TODO
 * @author: 丁乾文
 * @date:   2019年5月13日 下午8:46:41
 */
public class HttpInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 允許跨域
        response.setHeader("Access-Control-Allow-Origin", "*");
        // 允許自定義請求頭token(允許head跨域)
        response.setHeader("Access-Control-Allow-Headers", "token, Accept, Origin, X-Requested-With, Content-Type, Last-Modified");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}
package com.dqw.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.dqw.util.ResponseData;
/**
 * 首頁
 * @ClassName:  IndexController   
 * @Description:TODO
 * @author: 丁乾文
 * @date:   2019年5月13日 下午9:09:35
 */
@RestController
@RequestMapping("index")
public class IndexController {
	
	@GetMapping("index")
	public ResponseData toLogin() {
		ResponseData responseData = ResponseData.ok();
		return responseData;
	}
}


package com.dqw.controller;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.dqw.po.User;
import com.dqw.util.JWTUtil;
import com.dqw.util.ResponseData;
/**
 * @ClassName:  LoginController   
 * @Description:TODO
 * @author: 丁乾文
 * @date:   2019年5月13日 下午8:41:47
 */
@RestController
@RequestMapping("login")
public class LoginController {
	/**
	 * 用戶登錄
	 * @Title: login   
	 * @Description: TODO
	 * @param: @param user
	 * @param: @return      
	 * @return: ResponseData      
	 * @throws
	 */
    @PostMapping(value="login")
    public @ResponseBody ResponseData login(User user) {
    	//模擬去查詢數據庫,看是否存在此用戶
    	boolean login = toLogin(user);
    	ResponseData responseData = ResponseData.ok();
    	if(login) {
    		//生成token
    		String token = JWTUtil.generToken("1", "Jersey-Security-Basic", user.getEmail());
    		//向瀏覽器返回token,客戶端受到此token後存入cookie中,或者h5的本地存儲中
    		responseData.putDataValue("token", token);
    		//以及用戶
    		responseData.putDataValue("user", user);
    	}else {
    		//用戶或者密碼錯誤
    		responseData=ResponseData.customerError();
    	}
        return responseData;
    }
    public boolean toLogin(User user) {
    	if(user.getEmail()!=null&&user.getEmail().trim().length()>0) {
    		if(user.getEmail().equals("root")) {
    			if(user.getPassword().equals("123456")) {
    				return true;
    			}
    		}
    	}
    	return false;
    }
}

}


5.附springmvc配置文件

    <context:component-scan base-package="com"></context:component-scan>
    <!-- 啓動註解驅動 -->
    <mvc:annotation-driven/>
    <context:annotation-config></context:annotation-config>
    <mvc:interceptors>
    	<!-- 允許跨域 -->
     	<mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.dqw.interceptor.HttpInterceptor"></bean>
        </mvc:interceptor> 
    	<!-- 檢驗Token -->
         <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.dqw.interceptor.HeaderTokenInterceptor"></bean>
        </mvc:interceptor> 
     </mvc:interceptors>

6.login.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="jquery.cookie.js"></script>
</head>
<body>
	郵箱:<input type="text" name="email"><br>
	密碼:<input type="text" name="password"><br>
	<input type="submit" onclick="signIn()" value="登錄">
	<script type="text/javascript">
	/**
     * 登錄ajax,登錄成功後獲取後臺返回的token,並把token保存到cookie中
	 * 以後用戶每次請求後臺數據需要攜帶此token
     */
	function signIn() {
	    let email = $("input[name='email']").val();
	    let password = $("input[name='password']").val();
	    $.ajax({
	        url: "http://localhost:8080/jwt/login/login.action",
	        type: "POST",
	        dataType: "json",
	        data: {email: email, password: password},
	        success: function (result) {
				if(result.code==200){
					//保存token用來判斷用戶是否登錄,和身份是否屬實
					$.cookie('token', result.data.token);
					//$.cookie('user', result.data.user);
					//轉向主頁面
					location="index.html";
				}else{
					alert("用戶名或者密碼錯誤!");
				}
	        }
	    })
	}
	</script>
</body>
</html>

7.index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>歡迎</title>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="jquery.cookie.js"></script>
</head>
<body>
歡迎你(可以從本地存取獲取cookie中獲取)
<br>
<button onclick="logout()">註銷</button>
<script type="text/javascript">
    /**
     * 請求數據的ajax,需要從cookie讀取token放入head傳給後臺。
     */
    loadDeptTree();
	function loadDeptTree() {
	    $.ajax({
	        // 自定義的headers字段,會出現option請求,在GET請求之前,後臺要記得做檢驗。
	        headers: {
	            token: $.cookie('token')
	        },
	        url: "http://localhost:8080/jwt/index/index.action",
	        type: 'GET',
	        dataType: 'json',
	        success : function (result) {
				if(result.code==200){
					alert("加載到的數據:"+result+",並進行渲染頁面.....");
				}else{
					alert("異常,非法token,這裏不直接判斷是否token不對,實際開發需要各種判斷返回碼。");
				}
	        }
	    })
	}
    /**
     * 註銷,清空所有cookie(或者只清空保存着token的Cookie就行)
     */
	function logout() {
	    var keys = document.cookie.match(/[^ =;]+(?=\=)/g);
	    if(keys) {
	        for(var i = keys.length; i--;)
	            document.cookie = keys[i] + '=0;expires=' + new Date(0).toUTCString()
	    }
	    //返回登錄頁面或者主頁
	    window.location.href = "login.html";
	}
</script>
</body>
</html>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章