對於前后端分離的項目來說session來判斷是否登陸實現比較困難,token是比較好的方式。
大概流程:
1.用戶登陸,若成功則后台生成一個token,並把此token返回給客戶端瀏覽器
2.客戶端接收到token后,每次請求都要把此token放到header中發給后段
3.后段使用攔截器判斷token的正確性和實效性。
以下是具體代碼:
Token工具類:
package com.zsd.analyst.util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* JWT工具類
*
* @author SuperMu
* @Date: 2018/8/17 15:26
*/
public class JwtTokenUtil implements Serializable {
private static final long serialVersionUID = 6065288623015212853L;
private static final String CLAIM_KEY_CREATED = "created";
private static final String CLAIM_KEY_ID = "jti";
//#過期時間(秒) 目前有效期為10天
private static final long expiration = 15 * 60 * 1000;
/**
* token秘鑰,請勿泄露,請勿隨便修改
*/
private static final String secret = "JCKLJSDToasdlfj";
/**
* 從數據聲明生成令牌
*
* @param claims 數據聲明
* @return 令牌
*/
private static String generateToken(Map<String, Object> claims) {
Date expirationDate = new Date(System.currentTimeMillis() + expiration * 1000L);
return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret).compact();
}
/**
* 從令牌中獲取數據聲明
*
* @param token 令牌
* @return 數據聲明
*/
private static Claims getClaimsFromToken(String token) {
Claims claims;
try {
claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
} catch (Exception e) {
claims = null;
}
return claims;
}
/**
* 生成令牌
*
* @return 令牌
*/
public static String generateToken(Long userId) {
Map<String, Object> claims = new HashMap<String, Object>(2);
//claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
claims.put(CLAIM_KEY_ID, userId);
return generateToken(claims);
}
/**
* 從令牌中獲取用戶ID
*
* @param token 令牌
* @return id
*/
public static Long getIdFromToken(String token) {
Long id;
try {
Claims claims = getClaimsFromToken(token);
id = Long.valueOf(claims.getId());
} catch (Exception e) {
id = null;
}
return id;
}
/**
* 判斷令牌是否過期
*
* @param token 令牌
* @return 是否過期
*/
public static Boolean isTokenExpired(String token) {
try {
Claims claims = getClaimsFromToken(token);
Date expiration = claims.getExpiration();
return expiration.before(new Date());
} catch (Exception e) {
return false;
}
}
/**
* 刷新令牌
*
* @param token 原令牌
* @return 新令牌
*/
public static String refreshToken(String token) {
String refreshedToken;
try {
Claims claims = getClaimsFromToken(token);
claims.put("created", new Date());
refreshedToken = generateToken(claims);
} catch (Exception e) {
refreshedToken = null;
}
return refreshedToken;
}
/**
* 驗證令牌
*
* @param token 令牌
* @param userId 用戶Id
* @return 是否有效
*/
public static Boolean validateToken(String token, Long userId) {
Long id = getIdFromToken(token);
return (id.equals(userId) && !isTokenExpired(token));
}
public static void main(String[] args) {
System.out.println("得到用戶id=="+generateToken(1L));
}
}
注冊攔截器:
package com.zsd.analyst.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//@Configuration
public class LoginAdapter implements WebMvcConfigurer {
//解決跨域問題
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedHeaders("Content-Type","X-Requested-With","accept,Origin","Access-Control-Request-Method","Access-Control-Request-Headers","token")
.allowedMethods("*")
.allowedOrigins("*")
//是否允許使用cookie
.allowCredentials(true);
}
@Autowired
private TokenRefreshFilter tokenRefreshFilter;
// 這個方法是用來配置靜態資源的,比如html,js,css,等等
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
}
// 這個方法用來注冊攔截器,我們自己寫好的攔截器需要通過這里添加注冊才能生效
@Override
public void addInterceptors(InterceptorRegistry registry) {
System.out.println("進入攔截器");
//addPathPatterns是表明攔截哪些請求
//excludePathPatterns是對哪些請求不做攔截
registry.addInterceptor(tokenRefreshFilter).addPathPatterns("/**").excludePathPatterns("/login").excludePathPatterns("/backLogin");
}
}
攔截器:
package com.zsd.analyst.util;
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
//@Component
public class TokenRefreshFilter implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setCharacterEncoding("UTF-8");
Cookie[] tokens = request.getCookies();
String token = null;
for (Cookie cookie : tokens) {
if (cookie.getName().equals("token")){
token = cookie.getValue();
}
}
if (null == token) {
Map<String, Object> map = new HashMap<>();
map.put("data", "token is null");
map.put("code", "401");
response.getWriter().write(JSONObject.toJSONString(map));
return false;
} else {
boolean result = JwtTokenUtil.isTokenExpired(token);
if (result) {
//更新存儲的token信息
response.setHeader("token", JwtTokenUtil.refreshToken(token));
return true;
}
return true;
}
}
}
cookieUtil:
package com.zsd.analyst.util;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class CookieUtil {
public static final String TOKEN_NAME = "token";
public static final int TOKEN_EXPIRESECONDS = 1800;
//往瀏覽器寫入cookie信息
public static void addCookie(HttpServletResponse response, String cookieName, String cookieValue, int expireSeconds){
Cookie cookie = new Cookie(cookieName,cookieValue);
cookie.setDomain("localhost");//作用域,用戶請求哪個域名的時候回帶上該cookie信息,域名應該是nginx域名或者zuul域名
cookie.setPath("/");
cookie.setMaxAge(expireSeconds);
response.addCookie(cookie);
}
//從請求中獲取指定Cookie的信息.
public static String getCookie(HttpServletRequest request, String cookieName){
Cookie[] cookies = request.getCookies();
if(cookies==null || cookies.length==0){
//請求中沒有cookie的信息,不需要做任何的邏輯
return null;
}
for(Cookie cookie:cookies){
if(cookie.getName().equals(cookieName)){
return cookie.getValue();
}
}
return null;
}
}
登錄后把token加到cookie中
CookieUtil.addCookie(response,"token",JwtTokenUtil.generateToken(accountInfo.getId()),CookieUtil.TOKEN_EXPIRESECONDS);