微服务之间调用的安全认证之JWT<二>

初步 学习了解JWT后,进一步学习总结了微服务之间调用的安全认证。

一、创建统一认证服务

1、创建用户认证信息实体

@Data
@ToString
public static class TokenEntity {
  /** {@link LoginTypeEnum} */
  private String loginType;

  /** 角色 */
  private Integer role;

  /** 用户ID */
  private Long userId;

  /** token验证true:验证成功,false:验证失败 */
  private boolean verified;

  /** 迷离端登录openId */
  private String openId;
}

2、封装JWT工具类

  • 引入jwt依赖
<!-- Shiro+JWT start -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>${shiro.version}</version>
</dependency>

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>${jwt.version}</version>
</dependency>
<!-- Shiro+JWT end -->

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <jwt.version>3.8.3</jwt.version>
    <shiro.version>1.4.1</shiro.version>
</properties>
  • 使用JWT工具类进行认证主要有一下几个方法
    (1)生成token;
    (2)检查token是否合法;
    (3)刷新秘钥,HMAC256加密算法,生成签名
    HMAC是一种使用单向散列函数来构造消息认证码的方法,其中HMAC中的H就是Hash的意思。
    HMAC是密钥相关的哈希运算消息认证码,HMAC运算利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出。
    生成token是在进行用户身份验证之后,通过用户的ID,自定义openId,登录角色,登录端类型和过期时间,这个Token采用HMAC256加密方式进行加密,该算法需要一个秘钥。
/** JWT工具类*/
@Slf4j
public class JWTUtil {
  /** 秘钥 */
  private static final String SECRET_KEY = "54d54365-5342-7643-5gf4-rt43ac6424e3";
  /** token过期时间(单位:豪秒) */
  public static final long TOKEN_EXPIRE_TIME = 24 * 60 * 60 * 1000;
  //  public static final long TOKEN_EXPIRE_TIME = 3000; // token过期时间
  /** 签发人 */
  private static final String ISSUER = "issuer";

  private static String generateToken(
      final long userId, final Integer role, final String openId, final String loginType) {
    Date now = new Date();
    // 算法HMAC256
    Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY);
    String token =
        JWT.create()
            // 签发人
            .withIssuer(ISSUER)
            // 签发时间
            .withIssuedAt(now)
            // 过期时间
            .withExpiresAt(new Date(now.getTime() + TOKEN_EXPIRE_TIME))
            .withClaim("role", role)
            .withClaim("userId", userId)
            .withClaim("openId", openId)
            .withClaim("loginType", loginType)
            // 签名算法HMAC256
            .sign(algorithm);
    log.info("generateToken: 生成的token为 [{}]", token);
    return token;
  }

  public static String generateSaasToken(
      final long userId, final Integer role, final String openId, final String loginType) {
    return generateToken(userId, role, openId, loginType);
  }
  /** 验证token */
  public static boolean verify(String token) {
    try {
      log.info("verify:[验证token start]");
      Algorithm algorithm = Algorithm.HMAC256(SECRET_KEY); // 算法
      JWTVerifier verifier = JWT.require(algorithm).withIssuer(ISSUER).build();
      verifier.verify(token);
      return true;
    } catch (Exception e) {
      log.warn("verify:[{}]", e.getMessage());
      log.error(e.getMessage(), e);
      return false;
    }
  }
}

3、认证接口

  • 认证接口用于调用方法进行认证时,认证通过则返回一个加密的Token给对方,对方就可以用这个Token去请求别的服务了。
  • 认证代码清单如下
package yooo.yun.com.user.controller.mini;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import yooo.yun.com.common.api.ApiCode;
import yooo.yun.com.common.api.ApiResult;
import yooo.yun.com.common.entity.enums.LoginTypeEnum;
import yooo.yun.com.common.entity.pojo.user.UserPoJo;
import yooo.yun.com.common.entity.request.UserLoginReq;
import yooo.yun.com.common.entity.response.UserResponse;
import yooo.yun.com.common.utils.UUIDUtil;
import yooo.yun.com.user.service.UserService;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Objects;

/**
 * @author WangJiao
 * @since 2020/12/15
 */
@Slf4j
@RequestMapping(value = "/mini/user")
@RestController(value = "miniUserC")
@Api("MINI 用户API")
public class UserController {
  @Resource private UserService service;

  /**
   * 用户认证
   *
   * @param req req
   * @return res
   */
  @PostMapping("/auth-token")
  @ApiOperation("认证接口")
  public ApiResult authToken(@Valid @RequestBody UserLoginReq req) {
    log.info("authToken:[req:{}]", req);
    String tel = req.getTel();
    UserPoJo findUser = service.getByTel(tel);
    if (Objects.isNull(findUser)) {
      return ApiResult.fail(ApiCode.USER_ACCOUNT_NOT_EXIST);
    }
    if (!Objects.equals(
        DigestUtils.md5DigestAsHex(req.getPassword().getBytes()), findUser.getPassword())) {
      return ApiResult.fail(ApiCode.USER_PASSWORDS_ERROR);
    }
    String openId = UUIDUtil.getUUID(15);
    return ApiResult.ok(service.loginMiNi(findUser, LoginTypeEnum.MI_NI.getValue(), openId));
  }

  /**
   * 获取用户详情
   *
   * @param openId openId
   * @return res
   */
  @GetMapping("/detail")
  @ApiOperation("获取用户详情")
  public ApiResult detail(@RequestParam(value = "openId") String openId) {
    log.info("detail:[openId:{}]", openId);
    UserPoJo findUser = service.getByOpenId(openId);

    return Objects.nonNull(findUser)
        ? ApiResult.ok(UserResponse.of(findUser))
        : ApiResult.fail(ApiCode.USER_UNAUTHORIZED);
  }
}
  • 实现类认证代码清单
@Transactional(rollbackFor = Exception.class)
@Override
public String loginMiNi(UserPoJo findUser, String loginType, String openId) {
  findUser.setOpenId(openId);
  this.updateById(findUser);
  String token =
      JWTUtil.generateSaasToken(
          findUser.getId(), UserRoleEnum.ADMIN.getValue(), openId, loginType);
  log.info("loginMiNi:[token:{}]", token);
  return token;
}
  • 启动项目
    在这里插入图片描述
  • 数据库user表中目前有两个用户
    在这里插入图片描述
  • 使用postman访问认证接口
  • 访问一个不存在的用户,将给出相应的提示
    在这里插入图片描述
  • 使用正常信息访问,将返回加密的token
    在这里插入图片描述
  • 使用该token去访问其他微服务接口。
  • 当不使用token进行访问接口时,会提示凭证不能为空。
    在这里插入图片描述
  • 使用token去访问该接口,正常返回。
    在这里插入图片描述
  • 使用mini端的token去访问saas端的接口,同样也是没有权限访问的。
    在这里插入图片描述
  • 验证token失效,失效时间1分钟
/** token过期时间(单位:豪秒) */
public static final long TOKEN_EXPIRE_TIME = 60 * 1000;

在这里插入图片描述
-:到这里,相信你将get到了喔!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值