单点登录描述:
单点登录主要时应用在微服务架构中,在任意一个子服务中输入用户的用户名,密码进行登录时,
在跳转到其他系统的时候,就无需在进行登录,直接可以识别出用户的身份,权限以及角色等信息
.
.
JWT:全称java web token, 本质时一组加密后的字符串
JWT有三部分组成: header + payload+ sign (头+载荷+签名),
header记录: jwt使用的加密算法的类型,是一个json字符串,使用base64编码
payload载荷记录: 用户的用户名,用户角色,用户权限,jwt失效时间等循序,使用的时json字符串格式,使用了base64编码
sign签名: 包含了header+payload
JWT中三部分之间使用英文的句号隔开, 也就是 header头.paylouad载荷.sign签名
.
.
.
.
JWT实现单点登录的业务流程:
1,用户通过浏览器发送请求,访问网管,网管中有鉴权过滤器,校验之后,发现用户访问的是认证的微服务,则将请1求放行到认证微服务中
.
.
2,用户在认证微服务的登录页面中, 输入用户名, 密码, 然后登录. 登录后生成jwt字符
串, jwt字符串中有用的用户名, 用户的角色, 用户的权限信息, 返回保存到用户浏览器
cookie中
.
.
*3,用户访问其他服务时,携带token访问,每次访问校验token是否合法有效
JWT实现加密,解密:工具类
public class JwtUtil {
//有效期为
public static final Long JWT_TTL = 3600000L;// 60 * 60 *1000 一个小时
//设置秘钥明文
public static final String JWT_KEY = "qianfeng";
/**
* 创建token
* @param id
* @param subject
* @param ttlMillis
* @return
*/
public static String createJWT(String id, String subject, Long ttlMillis) {
/*获取加密类型*/
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
/*记录签发时间*/
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
if(ttlMillis==null){
ttlMillis=JwtUtil.JWT_TTL;
}
/*记录过期时间*/
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
/*拿到秘钥*/
SecretKey secretKey = generalKey();
/*将上面获取的变量全部设置到指定参数位置进行加密,并通过builder对象中的compact方法返回加密的字符串*/
JwtBuilder builder = Jwts.builder()
.setId(id) //唯一的ID
.setSubject(subject) // 主题 可以是JSON数据
.setIssuer("admin") // 签发者
.setIssuedAt(now) // 签发时间
.signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥
.setExpiration(expDate);// 设置过期时间
return builder.compact();
}
/**
* 解析
*
* @param jwt
* @return
* @throws Exception
*/
public static Claims parseJWT(String jwt) throws Exception {
/*获取秘钥
* 通过Jwts的parser方法,设置秘钥,设置前端传进来的加密的字符串jwt,底层内部实现对比*/
SecretKey secretKey = generalKey();
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(jwt)
.getBody();
}
/**
* 生成加密后的秘钥 secretKey
* @return
*/
public static SecretKey generalKey() {
/*对秘钥进行base64编码,防止乱码,生成字节数组
* 用SecretKeySpec对象创建密文(通过有参构造方法:参数1:字节数组,参数2-3:字节的起始和终止位置,参数3:加密的算法名字)*/
byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}
}
认证微服务模块的逻辑判断:
@Component
public class AuthFilter implements Ordered, GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
/*获取响应对象*/
ServerHttpRequest request = exchange.getRequest();
/*从请求中获取用户的url*/
ServerHttpResponse response = exchange.getResponse();
/*解析路径*/
String path = request.getURI().getPath();
if(StringUtils.isEmpty(path)){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
/*判断url是否是登录路劲或者是注册路径,是就直接无条件放行*/
if(path.contains("/auth/login")||path.contains("/auth/regist")){
/*放行*/
return chain.filter(exchange);
}
/*如果不是,则需要取jwt*/
MultiValueMap<String, HttpCookie> cookies = request.getCookies();
if(cookies==null){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
/*获取第一个名字叫做token的cookie值*/
HttpCookie cookie = cookies.getFirst("token");
if(cookie==null){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
/*判断是否能取取到jwt*/
String token = cookie.getValue();
if(StringUtils.isEmpty(token)){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
/*如果能取出来,则将解析jwt,然后放行*/
try {
Claims claims = JwtUtil.parseJWT(token);
} catch (Exception e) {
e.printStackTrace();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
return chain.filter(exchange);
}
注意,这里的放行配合网关实现请求分发到指定微服务上:
server:
port: 8082
spring:
application:
name: gateway
#注册中心
cloud:
nacos:
discovery:
server-addr: 192.168.200.129:8848
#配置网关(3个微服务模块)
gateway:
routes:
- id: customer
uri: lb://customer
predicates:
- Path=/consumer/**
- id: provider
uri: lb://provider
predicates:
- Path=/provide/**
- id: loginserver
uri: lb://loginserver
predicates:
- Path=/auth/**
案例的微服务模块