小白的深入总结Spring Cloud之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到了喔!