JWT是基于token的身份认证的方案。
json web token全称。可以保证安全传输的前提下传送一些基本的信息,以减轻对外部存储的依赖,减少了分布式组件的依赖,减少了硬件的资源。
可实现无状态、分布式的Web应用授权,jwt的安全特性保证了token的不可伪造和不可篡改。
本质上是一个独立的身份验证令牌,可以包含用户标识、用户角色和权限等信息,以及您可以存储任何其他信息(自包含)。任何人都可以轻松读取和解析,并使用密钥来验证真实性。
编写JWT工具类 JWTUtils
package com.fm.vege.base.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
public class JWTUtils {
private static final String SECRET = "QAZwsx1234";
private static final String ISS_USER = "jwt.auth";
private static final String ID_CLAIM = "id";
/**
* 默认过期2小时
*/
private static final long EXPIRE_TIME = 2 * 60 * 60 * 1000;
private static final Logger logger = LoggerFactory.getLogger(JWTUtils.class);
/**
* 生成用户token
* @param playLoad
* @return String
*/
public static String sign(PayLoadInfo playLoad){
Algorithm algorithm = Algorithm.HMAC256(SECRET);
if(playLoad.getExp() == null){
//设置默认过期时间
playLoad.setExp(System.currentTimeMillis() + EXPIRE_TIME);
}
if(playLoad.getUserId() == null){
logger.error("生成token失败,userId不得为空!");
return "";
}
if(playLoad.getUserId() == null){
logger.error("生成token失败,userType不得为空!");
return "";
}
if(playLoad.getUserId() == null){
logger.error("生成token失败,userName不得为空!");
return "";
}
String face = "";
if(playLoad.getFace() != null){
face = playLoad.getFace();
}
String token = JWT.create()
.withIssuer(ISS_USER).
withIssuedAt(new Date(System.currentTimeMillis())).
withExpiresAt(new Date(playLoad.getExp())).
withClaim("username",playLoad.getUsername()).
withClaim("face",face).
withClaim("userType",playLoad.getUserType()).
withClaim(ID_CLAIM,playLoad.getUserId()).
sign(algorithm);
return token;
}
/**
* 校验token
*
* @param token token值
* @return boolean
*/
public static boolean verifyToken(String token) {
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET))
.withIssuer(ISS_USER)
.build();
verifier.verify(token);
return true;
} catch (JWTVerificationException e) {
logger.error("verifyToken 校验token失败:{}",e.getMessage());
return false;
} catch (Exception e) {
return false;
}
}
/**
* 校验token 并返回 DecodedJWT
*
* @param token token值
* @return DecodedJWT
*/
public static DecodedJWT verifyAndReturn(String token) {
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET))
.withIssuer(ISS_USER).build();
return verifier.verify(token);
} catch (JWTVerificationException e) {
logger.error("verifyAndReturn 校验token失败:{},id = {}",e.getMessage());
return null;
} catch (Exception e) {
return null;
}
}
/**
* 校验并返回用户类型
* @param token
* @return Integer
*/
public static Integer verifyAndReturnUserType(String token) {
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET))
.withIssuer(ISS_USER).build();
return verifier.verify(token).getClaim("userType").asInt();
} catch (JWTVerificationException e) {
logger.error("verifyAndReturn 校验token失败:{},id = {}",e.getMessage());
return null;
} catch (Exception e) {
return null;
}
}
/**
* 校验token
*
* @param token token值
* @param id 用户的id
* @return boolean
*/
public static boolean verify(String token, Long id) {
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET))
.withIssuer(ISS_USER)
.withClaim(ID_CLAIM, id)
.build();
verifier.verify(token);
return true;
} catch (JWTVerificationException e) {
logger.error("verify 校验token失败:{},id = {}",e.getMessage(),id);
return false;
} catch (Exception e) {
return false;
}
}
/**
* 获取用户id
*
* @param token token值
* @return java.lang.Long
*/
public static Long getId(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim(ID_CLAIM).asLong();
} catch (JWTDecodeException e) {
logger.error("获取用户id失败:{}",e.getMessage());
return null;
} catch (Exception e) {
return null;
}
}
/**
* 获取颁发时间
*
* @param token token值
* @return java.util.Date
*/
public static Date getIssuedDate(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getIssuedAt();
} catch (JWTDecodeException e) {
logger.error("获取颁发时间失败:{}",e.getMessage());
return null;
} catch (Exception e) {
return null;
}
}
/**
* 获取过期时间
*
* @param token token值
* @return java.util.Date
*/
public static Date getExpireDate(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getExpiresAt();
} catch (JWTDecodeException e) {
logger.error("获取过期时间失败:{}",e.getMessage());
return null;
} catch (Exception e) {
return null;
}
}
/**
* 判断是否过期
*
* @param token token值
* @return boolean
*/
public static boolean isExpire(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getExpiresAt().compareTo(new Date()) <= 0 ? true : false;
} catch (JWTDecodeException e) {
return true;
} catch (Exception e) {
return true;
}
}
}
PayLoadInfo:
package com.fm.vege.base.util;
import lombok.Data;
@Data
public class PayLoadInfo {
/**
* iss: 该JWT的签发者,是否使用是可选的;
* sub: 该JWT所面向的用户,是否使用是可选的;
* aud: 接收该JWT的一方,是否使用是可选的;
* exp(expires): 什么时候过期,这里是一个Unix时间戳,是否使用是可选的;
* iat(issued at): 在什么时候签发的(UNIX时间),是否使用是可选的;
* 其他还有:
* nbf (Not Before):如果当前时间在nbf里的时间之前,则Token不被接受;一般都会留一些余地,比如几分钟;,是否使用是可选的;
*/
/**
* 必填
*/
private Long userId;
/**
* 用户类型 1 管理员 2 卖家 3 普通用户
*/
private Integer userType;
/**
* 用户名
*/
private String username;
/**
* 头像地址
*/
private String face;
/**
* 可选
*/
private String iss;
/**
* 可选
*/
private Long exp;
/**
* 可选
*/
private Long iat;
}
登录成功后生成token签名
网页用户登录成功后,生成并返回token,然后存储到浏览器cookie,以后每次请求需要携带该token进行身份认证
@Override
public Rsp<SellerLoginBO> loginSeller(LoginReqBO reqBO) {
if(reqBO.getUsername() == null || reqBO.getPassword() == null){
throw new BusinessException(BaseConstants.RSP_ERROR,"用户名或密码不得为空!");
}
SellerPO sellerPO = new SellerPO();
sellerPO.setLoginName(reqBO.getUsername());
sellerPO.setLoginPwd(reqBO.getPassword());
SellerPO seller = sellerMapper.selectByLoginNameAndPass(sellerPO);
if(seller == null){
throw new BusinessException(BaseConstants.RSP_ERROR,"用户名或密码不正确!");
}else{
PayLoadInfo payLoadInfo = new PayLoadInfo();
payLoadInfo.setUserId(seller.getSellerId());
payLoadInfo.setUserType(DictionaryUserField.USER_TYPE_SELLER.getValue());
payLoadInfo.setUsername(sellerPO.getLoginName());
//模拟用户头像
if(sellerPO.getSellerPhoto() == null){
payLoadInfo.setFace("/img/face.jpg");
}else{
payLoadInfo.setFace(sellerPO.getSellerPhoto());
}
String token = JWTUtils.sign(payLoadInfo);
if("".equals(token)){
throw new BusinessException(BaseConstants.RSP_ERROR,"登录失败,token创建失败!");
}
SellerLoginBO loginBO = new SellerLoginBO();
Rsp<SellerLoginBO> rsp = new Rsp<>();
BeanUtils.copyProperties(seller,loginBO);
loginBO.setToken(token);
rsp.setData(loginBO);
rsp.setRspCode(BaseConstants.RSP_SUCCESS);
rsp.setRspDesc("login successful!");
return rsp;
}
}
编写网关拦截器,进行身份认证
在zuul-router工程新建拦截器
@Component
public class AuthFilter extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(AuthFilter.class);
@Override
public String filterType() {
/**
* 这里很重要,设置拦截类型为post请求之前
*/
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
/**
* 拦截器到优先级,数字越大的优先执行
*/
return 1;
}
/**
* 定义忽略的一些请求,比如登录之类的请求 不需要走拦截器认证
*/
private static List<String> ignoreApi = null;
static {
ignoreApi = Lists.newArrayList();
ignoreApi.add("/user-api/api/login/admin");
ignoreApi.add("/user-api/api/login/seller");
ignoreApi.add("/user-api/api/register/seller");
ignoreApi.add("/user-api/api/register/user");
ignoreApi.add("/user-api/api/sms/send");
ignoreApi.add("/user-api/api/sms/get");
}
@Override
public boolean shouldFilter() {
//共享RequestContext,上下文对象
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
logger.info("request uri : {}",request.getRequestURI());
//不需要权限校验URL
for (String api : ignoreApi) {
if (api.equalsIgnoreCase(request.getRequestURI())) {
return false;
}
}
return true;
}
@Override
public Object run() throws ZuulException {
//JWT
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
//token对象,有可能在请求头传递过来,也有可能是通过参数传过来,实际开发一般都是请求头方式
String token = request.getHeader("token");
if (StringUtils.isBlank((token))) {
token = request.getParameter("token");
}
logger.info("页面传来的token值为:{}", token);
//登录校验逻辑 如果token为null,则直接返回客户端,而不进行下一步接口调用
if (StringUtils.isBlank(token)) {
// 过滤该请求,不对其进行路由
requestContext.setSendZuulResponse(false);
//返回错误代码
requestContext.setResponseStatusCode(401);
return null;
} else {
String requestURI = request.getRequestURI();
Integer userType = JWTUtils.verifyAndReturnUserType(token);
if (userType == null) {
logger.info("非法请求:{}", request.getRequestURI());
// 过滤该请求,不对其进行路由
requestContext.setSendZuulResponse(false);
//返回错误代码
requestContext.setResponseStatusCode(401);
return null;
}
logger.info("用户类型:{} , userType = {} ", DictionaryUserField.getDescByValue(userType),userType);
//管理员
if (DictionaryUserField.USER_TYPE_ADMIN.getValue().equals(userType)) {
} else if (DictionaryUserField.USER_TYPE_SELLER.getValue().equals(userType)) {
//卖家
if (request.getRequestURI().contains("/api/admin")) {
logger.info("非法请求,禁止普通用户访问接口:{}", request.getRequestURI());
// 过滤该请求,不对其进行路由
requestContext.setSendZuulResponse(false);
//返回错误代码
requestContext.setResponseStatusCode(401);
return null;
}
} else if (DictionaryUserField.USER_TYPE_PT.getValue().equals(userType)) {
//普通用户
}
return null;
}
}
}



