文章目录
- jwt + shiro 认证
- jwt 使用
- 引入依赖
- 编写 jwt 工具类
- 测试
- shiro + jwt 认证
- 项目文件路径:
- 引入依赖
- shiro.ini
- 重写 Realm
- 认证服务接口
- 认证服务接口实现类
- 自定义 token
- 测试
代码来自于小傅哥 《API网关》 项目
jwt + shiro 认证
jwt 使用
引入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
编写 jwt 工具类
注意 signingKey 不要暴露出去。
package cn.bugstack.gateway.authorization;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author 小傅哥,微信:fustack
* @description JWT(JSON Web Tokens)https://jwt.io/
* @github https://github.com/fuzhengwei
* @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
*/
public class JwtUtil {
private static final String signingKey = "B*B^5Fe";
/**
* 生成 JWT Token 字符串
*
* @param issuer 签发人
* @param ttlMillis 有效期
* @param claims 额外信息
* @return Token
*/
public static String encode(String issuer, long ttlMillis, Map<String, Object> claims) {
if (null == claims) {
claims = new HashMap<>();
}
// 签发时间(iat):荷载部分的标准字段之一
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
// 签发操作
JwtBuilder builder = Jwts.builder()
// 荷载部分
.setClaims(claims)
// 签发时间
.setIssuedAt(now)
// 签发人;类似 userId、userName
.setSubject(issuer)
// 设置生成签名的算法和秘钥
.signWith(SignatureAlgorithm.HS256, signingKey);
if (ttlMillis >= 0) {
long expMillis = nowMillis + ttlMillis;
Date exp = new Date(expMillis);
// 过期时间(exp):荷载部分的标准字段之一,代表这个 JWT 的有效期。
builder.setExpiration(exp);
}
return builder.compact();
}
public static Claims decode(String token) {
return Jwts.parser()
// 设置签名的秘钥
.setSigningKey(signingKey)
// 设置需要解析的 jwt
.parseClaimsJws(token)
.getBody();
}
}
测试
@Test
public void test_awt() {
String issuer = "xiaofuge";
long ttlMillis = 7 * 24 * 60 * 60 * 1000L;
Map<String, Object> claims = new HashMap<>();
claims.put("key", "xiaofuge");
// 编码
String token = JwtUtil.encode(issuer, ttlMillis, claims);
System.out.println(token);
// 解码
Claims parser = JwtUtil.decode(token);
System.out.println(parser.getSubject());
}
shiro + jwt 认证
项目文件路径:
引入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
shiro.ini
[main]
# 声明1个Realm,也可以声明多个,多个则顺序执行
gatewayAuthorizingRealm=cn.bugstack.gateway.authorization.GatewayAuthorizingRealm
# 指定 securityManager 的 realms 实现。如果是多个则用逗号隔开。
securityManager.realms=$gatewayAuthorizingRealm
重写 Realm
在 realm 中进行认证操作,即拿到 token 进行 jwt 解密,如果解密成功,说明认证成功
package cn.bugstack.gateway.authorization;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* @author 小傅哥,微信:fustack
* @description 验证领域
* @github https://github.com/fuzhengwei
* @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
*/
public class GatewayAuthorizingRealm extends AuthorizingRealm {
@Override
public Class<?> getAuthenticationTokenClass() {
return GatewayAuthorizingToken.class;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 暂时不需要做授权处理
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
try {
// 验证解析是否报错
JwtUtil.decode(((GatewayAuthorizingToken) token).getJwt());
} catch (Exception e) {
throw new AuthenticationException("无效令牌");
}
return new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), this.getName());
}
}
认证服务接口
/**
* @author 小傅哥,微信:fustack
* @description 认证服务接口
* @github https://github.com/fuzhengwei
* @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
*/
public interface IAuth {
boolean validate(String id, String token);
}
认证服务接口实现类
做两件事:初始化 shiro 和 shiro 认证
-
使用 ini 初始化 shiro 可参考官网:https://shiro.apache.org/configuration.html
-
validate()
: 在这里进行 shiro 的认证,认证时传入我们自己实现的GatewayAuthorizingToken
,里边存储的有被认证用户传进的 jwt 的 token,拿到之后会到我们自定义的GatewayAuthorizingRealm
中进行认证操作,即对GatewayAuthorizingToken
中传入的 token 使用 jwt 进行解密,如果解密成功,则认证成功
/**
* @author 小傅哥,微信:fustack
* @description 认证服务实现
* @github https://github.com/fuzhengwei
* @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
*/
public class AuthService implements IAuth {
private Subject subject;
public AuthService() {
// 1. 获取 SecurityManager 工厂,此处使用 shiro.ini 配置文件初始化 SecurityManager
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
// 2. 得到 SecurityManager 实例 并绑定给 SecurityUtils
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
// 3. 得到 Subject 及 Token
this.subject = SecurityUtils.getSubject();
}
@Override
public boolean validate(String id, String token) {
try {
// 身份验证
subject.login(new GatewayAuthorizingToken(id, token));
// 返回结果
return subject.isAuthenticated();
} finally {
// 退出
subject.logout();
}
}
}
自定义 token
在自定义 token 中存储 jwt 认证信息
/**
* @author 小傅哥,微信:fustack
* @description 验证 Token
* @github https://github.com/fuzhengwei
* @Copyright 公众号:bugstack虫洞栈 | 博客:https://bugstack.cn - 沉淀、分享、成长,让自己和他人都能有所收获!
*/
public class GatewayAuthorizingToken implements AuthenticationToken {
private static final long serialVersionUID = 1L;
// 通信管道ID
private String channelId;
// JSON WEB TOKEN
private String jwt;
public GatewayAuthorizingToken() {
}
public GatewayAuthorizingToken(String channelId, String jwt) {
this.channelId = channelId;
this.jwt = jwt;
}
@Override
public Object getPrincipal() {
return channelId;
}
@Override
public Object getCredentials() {
return this.jwt;
}
public String getChannelId() {
return channelId;
}
public void setChannelId(String channelId) {
this.channelId = channelId;
}
public String getJwt() {
return jwt;
}
public void setJwt(String jwt) {
this.jwt = jwt;
}
}
测试
@Test
public void test_auth_service() {
IAuth auth = new AuthService();
// 这里的 token 使用 jwt 工具类的 encode() 方法生成的 token
boolean validate = auth.validate("123", "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ4aWFvZnVnZSIsImV4cCI6MTY5MTY0NTM3NSwiaWF0IjoxNjkxMDQwNTc1LCJrZXkiOiJ4aWFvZnVnZSJ9.fy8Rc5d_w6JX1QRIBuEeni8fgtDYFVyCBsnukuPXrlc");
System.out.println(validate ? "验证成功" : "验证失败");
}