不积跬步,无以至千里;不积小流,无以成江海。

spring cloud 项目实战 – 基于jwt的token认证

网站建设 康康 2334℃ 0评论

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;
        }
    }
}

 

转载请注明:左手代码右手诗 » spring cloud 项目实战 – 基于jwt的token认证

喜欢 (0)or分享 (0)
发表我的评论
取消评论

 

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址