Jwt加filter实现app认证

导入jwt依赖

  		<dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

token实体类

@Data
public class TokenDto implements Serializable {

    /**
     * token
     */
    private String token;

    /**
     * 到期时间
     */
    private String expire;

}

封装jwt生成token工具类

/**
 * jwt生成、验证工具
 * 注意点:
 *  1、生成的Token可以通过base64解密出明文信息
 *  2、base64解密出的明文信息,修改后再进行编码会解密失败(核心)
 *  3、无法作废已颁布的token,除非改密钥
 */
public class JwtUtils {

    /**
     * 过期时间,一周
     */
    private static final long EXPIRE = 1000 * 60 * 60 * 24 * 7;

    /**
     * SECRET 是签名密钥,只生成一次即可,生成方法:
     * Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
     * String secretString = Encoders.BASE64.encode(key.getEncoded()); # 本文使用 BASE64 编码
     * */
    private static final String SECRET = "PVjALNPPuVzHQaPrGQJJ94r3u0ZoL2hcnEUPAcOAwkM=";

    /**
     * 主题
     */
    private static final String SUBJECT = "enterprise";

    /**
     * 负载部分自定义数据的key
     */
    public static final String CUSTOM_PAYLOAD_PHONE_KEY = "phone";
    public static final String CUSTOM_PAYLOAD_TYPE_KEY = "type";
    public static final String CUSTOM_PAYLOAD_TENANTID_KEY = "tenantId";

    /**
     * 令牌类型
     */
    public static final String TYPE_APPLETS = "applets";
    public static final String TYPE_WEB = "web";

    /**
     * 根据自定义信息生成令牌
     * @param type      请求类型,app、web
     * @param tenantId  租户ID
     * @param phone     手机号
     * @return
     */
    public static TokenDto getToken(String type, String tenantId, String phone){
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        TokenDto dto = new TokenDto();
        Date exp = new Date(System.currentTimeMillis()+EXPIRE);
        String token = Jwts.builder()
                .setSubject(SUBJECT)
                // 自定义内容
                .claim(CUSTOM_PAYLOAD_TYPE_KEY,type)
                .claim(CUSTOM_PAYLOAD_TENANTID_KEY,tenantId)
                .claim(CUSTOM_PAYLOAD_PHONE_KEY,phone)
                // 令牌颁发时间
                .setIssuedAt(new Date())
                // 令牌过期时间
                .setExpiration(exp)
                // 签名加密算法 和 密钥
                .signWith(signatureAlgorithm,getSecretKey())
                // 返回字符串
                .compact();
        dto.setToken(token);
        dto.setExpire(TimeUtil.dateToStamp2(exp));
        return dto;
    }

    /**
     * Base64.getDecoder().decode(getBase64Security())
     * 检查token有效性,token校验失败会抛异常
     * @param   token
     * @return  返回null即校验失败
     */
    public static Claims checkJwt(String token){
        //签名秘钥,和生成的签名的秘钥一模一样
        SecretKey key = getSecretKey();
        Claims claims;
        try {
            claims = Jwts.parser()  //得到DefaultJwtParser
                    .setSigningKey(key)         //设置签名的秘钥
                    .parseClaimsJws(token).getBody();
        } catch (Exception e) {
            claims = null;
        }//设置需要解析的jwt
        return claims;
    }

    /**
     * SecretKey 根据 SECRET 的编码方式解码后得到:
     * Base64 编码:SecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretString));
     * Base64URL 编码:SecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64URL.decode(secretString));
     * 未编码:SecretKey key = Keys.hmacShaKeyFor(secretString.getBytes(StandardCharsets.UTF_8));
     * */
    private static SecretKey getSecretKey() {
        byte[] encodeKey = Base64.decodeBase64(SECRET);
        SecretKey key = new SecretKeySpec(encodeKey, 0, encodeKey.length, "AES");
        return key;
    }

登录过滤器实现Filter接口

/**
 * 登录过滤器
 */
@AllArgsConstructor
@WebFilter(urlPatterns = "/app/api/v1/pri/*",filterName = "loginFilter")
public class LoginFilter implements Filter {

    private final RedisUtil redisUtil ;

    /**
     * 容器加载时执行
     * @param filterConfig
     * @throws ServletException
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

        System.out.println("login filter init...");

    }

    /**
     * 每次请求都会调用,如果返回提示信息始终不变,可以简化
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("login filter doFilter...");

        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;
        String token = req.getHeader(AppConstant.APP_TOKEN_HEADER);
        if(StringUtils.isNotEmpty(token)){
            // 1、校验token
            Claims claims = JwtUtils.checkJwt(token);
            if(null != claims){
                String phone = (String)claims.get(JwtUtils.CUSTOM_PAYLOAD_PHONE_KEY);
                if(StringUtils.isNotEmpty(phone)){
                    // 2、判断缓存
                    String redisToken = redisUtil.get(AppConstant.APP_TOKEN_REDIS_PREFIX + phone);
                    if(StringUtils.isNotEmpty(redisToken) && StringUtils.equals(token,redisToken)){
                        // 3、更新token缓存时间
                        redisUtil.setEx(AppConstant.APP_TOKEN_REDIS_PREFIX+phone,token,AppConstant.APP_TOKEN_EXPIRE);
                        // 4、继续执行后续链路
                        filterChain.doFilter(servletRequest,servletResponse);
                    }else {
                        renderJson(resp,R.fail(401,"请求未授权"));
                    }
                }else {
                    renderJson(resp,R.fail(401,"请求未授权"));
                }
            }else {
                renderJson(resp,R.fail(401,"请求未授权"));
            }
        }else {
            renderJson(resp,R.fail(401,"请求未授权"));
        }
    }

    /**
     * 容器销毁的时候执行
     */
    @Override
    public void destroy() {
        System.out.println("login filter destroy...");
    }

    /**
     * 返回JSON数据
     * @param response
     * @param json
     */
    private void renderJson(HttpServletResponse response, R<Object> json){
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        try (PrintWriter writer = response.getWriter()){
            writer.print(JSON.toJSONString(json));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

启动类上要加@ServletComponentScan注解

image-20210719170803873

登录接口示列

    @PostMapping("/login")
    @ApiOperationSupport(order = 2)
    @ApiOperation(value = "物业APP登录", notes = "手机号和密码")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "data", value = "手机号和密码加密后的字符串", paramType = "query", dataType = "string", required = true),
            @ApiImplicitParam(name = "tenantId", value = "租户ID", paramType = "header", dataType = "string", required = true)
    })
    public String loginApp(HttpServletRequest request,String data) {
        PropertyAppUser appUser = AesPkcs5Util.pareData(data, new PropertyAppUser(),apiCryptoProperties.getAesKey());
        String tenantId = getTenantId(request);
        if (null == appUser || StringUtils.isEmpty(tenantId) || StringUtils.isEmpty(appUser.getAccount()) || StringUtils.isEmpty(appUser.getPassword())) {
            return AesPkcs5Util.encryptResponseData(R.fail("参数错误!"),apiCryptoProperties.getAesKey());
        }
        PropertyAppUser user = propertyAppUserService.getByPhone(appUser);
        if (null == user) {
            return AesPkcs5Util.encryptResponseData(R.fail("用户名或密码错误!"),apiCryptoProperties.getAesKey());
        }
        // 将传过来的密码加密
        String lowerPwd = DigestUtil.encrypt(appUser.getPassword());
        if (StringUtils.equals(user.getPassword(), lowerPwd)) {
            String oldToken = redisUtil.get(AppConstant.APP_TOKEN_REDIS_PREFIX + appUser.getAccount());
            if (StringUtils.isNotEmpty(oldToken)) {
                redisUtil.del(AppConstant.APP_TOKEN_REDIS_PREFIX + appUser.getAccount());
            }
            // 构造新的token,放入缓存
            TokenDto dto = JwtUtils.getToken(JwtUtils.TYPE_APP, tenantId, appUser.getAccount());
            //设置key和值还有过期时间
            redisUtil.setEx(AppConstant.APP_TOKEN_REDIS_PREFIX + appUser.getAccount(), dto.getToken(), AppConstant.APP_TOKEN_EXPIRE);
            // TODO 目前仅返回token,需要封装为app需要的包装类
            Map<String,Object> map = propertyAppUserService.loginAppUser(user);
            map.put("token",dto.getToken());
            map.put("expire",dto.getExpire());
            return AesPkcs5Util.encryptResponseData(R.data(map,"登录成功!"),apiCryptoProperties.getAesKey());
        } else {
            return AesPkcs5Util.encryptResponseData(R.fail("用户名或密码错误!"),apiCryptoProperties.getAesKey());
        }
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值