文章目录
- 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 ? "验证成功" : "验证失败");
}


















