Gateway、JWT

单体架构的时候,登陆操作需要整合shiro。

而现在到了微服务阶段,我们需要在各个服务中都整合shiro,这样就做了很多重复的工作。
这时就用到了网关 ,网关 是微服务项目的唯一入口。
之前是前端请求直接访问controller层,现在是先访问Gateway,所以 API ⽹关的通常作⽤是完成⼀些通⽤的功能,如请求认证,请求记录,请求限流,⿊⽩名单判断等。
网关就像一个大楼的大门。

那网关中的UserController怎么调用UserService呢?因为这两个服务是两个单独的进程,进程之间的通信,就用到了Dubbo, 也就是说将UserService中的服务暴露出来,在网关中进行调用。
在这里插入图片描述
zookeeper进行:管理和服务发现

网关是一个单独的服务,可以和下图中的其他服务在不同的机器上部署。
在这里插入图片描述


JWT
在这里插入图片描述

http协议是无状态的,这种特性意味着程序需要验证每一次请求来辨别客户端的身份。

cookie、session、token区别:
参看博客:https://www.cnblogs.com/moyand/p/9047978.html

http和https的联系与区别:
https://zhuanlan.zhihu.com/p/72616216
https://www.cnblogs.com/wqhwe/p/5407468.html

如何使用JWT:
在这里插入图片描述

JwtTokenUtils:

package com.mall.user.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.mall.commons.tool.exception.ValidateException;
import com.mall.user.constants.SysRetCodeConstants;

import lombok.Builder;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import org.joda.time.DateTime;

/**
 * create by ciggar on 2020/04/08
 */
@Slf4j
@Builder  // lombok提供的注解
public class JwtTokenUtils {
    /**
     * 传输信息,必须是json格式,可以用来存放用户信息
     */
    private String msg;
    /**
     * 所验证的jwt
     */
    @Setter
    private String token;

    // 密钥
    private final String secret = "324iu23094u598ndsofhsiufhaf_+0wq-42q421jiosadiusadiasd";

    public String creatJwtToken() {
        msg = new AESUtil(msg).encrypt();//先对信息进行aes加密(防止被破解) AES 对称加密
        String token = null;
        try {
            token = JWT.create()
                    .withIssuer("ciggar").withExpiresAt(DateTime.now().plusDays(1).toDate())
                    .withClaim("user", msg)
                    // sign表示指定加密算法
                    .sign(Algorithm.HMAC256(secret));
        } catch (Exception e) {
            throw e;
        }
        log.info("加密后:" + token);
        return token;
    }

    public static void main(String[] args) {
        // 加密
        // 每次产生的token都不一样,因为签发时间和token的过期时间都是不一样的。
//        String token = JwtTokenUtils.builder().msg("cskaoyan").build().creatJwtToken();
//        System.out.println("产生的token:" + token);

        // 解密
        String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJjaWdnYXIiLCJleHAiOjE1ODc5ODE1MDEsInVzZXIiOiIwQTgzNDAxMjA2QjA5RDY3NTJCMUJDRDk5QkIwQTMwMiJ9.6_izud3xe7Fypthm5i-Z58aylIqIGU_Pq0uxzagFsrw";

        String info = JwtTokenUtils.builder().token(token).build().freeJwt();
        System.out.println(info); 

    }


    /**
     * 解密jwt并验证是否正确
     */
    public String freeJwt() {
        DecodedJWT decodedJWT = null;
        try {
            //使用hmac256加密算法
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(secret))
                    .withIssuer("ciggar")
                    .build();
            decodedJWT = verifier.verify(token);
            log.info("签名人:" + decodedJWT.getIssuer() + " 加密方式:" + decodedJWT.getAlgorithm() + " 携带信息:" + decodedJWT.getClaim("user").asString());
        } catch (Exception e) {
            log.info("jwt解密出现错误,jwt或私钥或签证人不正确");
            throw new ValidateException(SysRetCodeConstants.TOKEN_VALID_FAILED.getCode(), SysRetCodeConstants.TOKEN_VALID_FAILED.getMessage());
        }
        //获得token的头部,载荷和签名,只对比头部和载荷
        String[] headPayload = token.split("\\.");
        //获得jwt解密后头部
        String header = decodedJWT.getHeader();
        //获得jwt解密后载荷
        String payload = decodedJWT.getPayload();
        if (!header.equals(headPayload[0]) && !payload.equals(headPayload[1])) {
            throw new ValidateException(SysRetCodeConstants.TOKEN_VALID_FAILED.getCode(), SysRetCodeConstants.TOKEN_VALID_FAILED.getMessage());
        }
        return new AESUtil(decodedJWT.getClaim("user").asString()).decrypt();
    }

}

AESUtils:

package com.mall.user.utils;

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

import java.io.IOException;
import java.io.InputStream;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Properties;

/**
 * ciggar
 * create-date: 2019/7/22-17:41
 */
@Slf4j
public class AESUtil {
    //加密或解密内容
    @Setter
    private String content;
    //加密密钥
    private String secret;

    public AESUtil(String content) {
        this.content = content;
        this.secret = "iwofnoadnsa922342mnjaolkdsao9423242niosadopa_a02402sad";
    }

    /**
     * 加密
     *
     * @return 加密后内容
     */
    public String encrypt() {
        Key key = getKey();
        byte[] result = null;
        try {
            //创建密码器
            Cipher cipher = Cipher.getInstance("AES");
            //初始化为加密模式
            cipher.init(Cipher.ENCRYPT_MODE, key);
            //加密
            result = cipher.doFinal(content.getBytes("UTF-8"));
        } catch (Exception e) {
            log.info("aes加密出错:" + e);
        }
        //将二进制转换成16进制
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < result.length; i++) {
            String hex = Integer.toHexString(result[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }
        return sb.toString();
    }

    /**
     * 解密
     *
     * @return 解密后内容
     */
    public String decrypt() {
        //将16进制转为二进制
        if (content.length() < 1)
            return null;
        byte[] result = new byte[content.length() / 2];
        for (int i = 0; i < content.length() / 2; i++) {
            int high = Integer.parseInt(content.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(content.substring(i * 2 + 1, i * 2 + 2), 16);
            result[i] = (byte) (high * 16 + low);
        }

        Key key = getKey();
        byte[] decrypt = null;
        try {
            //创建密码器
            Cipher cipher = Cipher.getInstance("AES");
            //初始化为解密模式
            cipher.init(Cipher.DECRYPT_MODE, key);
            //解密
            decrypt = cipher.doFinal(result);
        } catch (Exception e) {
            log.info("aes解密出错:" + e);
        }
        assert decrypt != null;
        return new String(decrypt);
    }

    /**
     * 根据私钥内容获得私钥
     */
    private Key getKey() {
        SecretKey key = null;
        try {
            //创建密钥生成器
            KeyGenerator generator = KeyGenerator.getInstance("AES");
            //初始化密钥
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
            random.setSeed(secret.getBytes());
            generator.init(128, random);
            //生成密钥
            key = generator.generateKey();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return key;
    }

    public static void main(String[] args) {
        AESUtil aesUtil = new AESUtil("Hello");
        String ec = aesUtil.encrypt();
        System.out.println(ec);
        System.out.println(new AESUtil(ec).decrypt());
    }
}

TokenIntercepter:

package com.mall.user.intercepter;

import com.alibaba.fastjson.JSON;
import com.mall.commons.result.ResponseData;
import com.mall.commons.result.ResponseUtil;
import com.mall.commons.tool.utils.CookieUtil;
import com.mall.user.annotation.Anoymous;
import com.mall.user.constants.SysRetCodeConstants;
import com.mall.user.dto.CheckAuthRequest;
import com.mall.user.dto.CheckAuthResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

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

import java.lang.reflect.Method;

/**
 * 用来实现token拦截认证
 * <p>
 * 其实就是用来判断当前这个操作是否需要登录
 */
public class TokenIntercepter extends HandlerInterceptorAdapter {

    @Reference(timeout = 3000,check = false)
    ILoginService iUserLoginService;

    public static String ACCESS_TOKEN = "access_token";

    public static String USER_INFO_KEY = "userInfo";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // handler就是要访问的目标方法
        // 如果handler不是HandlerMethod类型,就直接放行
        // 什么样的handler不是HandlerMethod类型呢? 静态资源
        // 如果访问的是静态资源,直接放行
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        // Object bean = handlerMethod.getBean();
        // 判断是否需要登录,如果不需要,直接放行
        if (isAnoymous(handlerMethod)) {
            return true;
        }
        // 从cookie里面去取token
        String token = CookieUtil.getCookieValue(request, ACCESS_TOKEN);
        if (StringUtils.isEmpty(token)) {
            ResponseData responseData = new ResponseUtil().setErrorMsg("token已失效");
            response.setContentType("text/html;charset=UTF-8");
            response.getWriter().write(JSON.toJSON(responseData).toString());
            return false;
        }

        //从token中获取用户信息
        CheckAuthRequest checkAuthRequest = new CheckAuthRequest();
        checkAuthRequest.setToken(token);

        // token获取到了,怎么去验证呢?
        // 其实就是使用 JwtUtils.freeJwt()把这个token给解密
        CheckAuthResponse checkAuthResponse = iUserLoginService.validToken(checkAuthRequest);
        if (checkAuthResponse.getCode().equals(SysRetCodeConstants.SUCCESS.getCode())) {
            // 放到了Request域中
            request.setAttribute(USER_INFO_KEY, checkAuthResponse.getUserinfo()); //保存token解析后的信息后续要用
            return true;
        }
        ResponseData responseData = new ResponseUtil().setErrorMsg(checkAuthResponse.getMsg());
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().write(JSON.toJSON(responseData).toString());
        return false;
    }

    private boolean isAnoymous(HandlerMethod handlerMethod) {
        Object bean = handlerMethod.getBean();
        // 获取目标方法的类对象
        Class clazz = bean.getClass();
        // 如果类上有@Anoymous注解,返回true
        if (clazz.getAnnotation(Anoymous.class) != null) {
            return true;
        }
        // 获取方法对象
        Method method = handlerMethod.getMethod();
        // 如果方法上有@Anoymous注解,返回true,否则返回false
        return method.getAnnotation(Anoymous.class) != null;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-玫瑰少年-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值