SpringBoot 上Shiro结合JWT的鉴权方式

【目前仅做了简单的记录】

最近碰到的项目属于完全的前后端分离项目,鉴权机制应是无状态的。
大致配置的过程有两部分:

  1. 登陆验证,配置JWT签发与校验Token
  2. 关闭Shiro框架的Session与Cookie,并在Shiro中配置请求过滤器

登录验证

先说登录验证,
登录验证中都需要用到Token的签发与校验方法,所以封装一个JWT的Util,这里用的是auth0的jwt包

public class JWTUtil {

    private static final long EXPIRE_TIME = 60*60*1000;

    public static boolean verify(String token, String username, String secret) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(secret);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withClaim("username", username)
                    .acceptLeeway(15)
                    .build();
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (SignatureVerificationException e) {
            return false;
        } catch (TokenExpiredException e) {
            return false;
        }
    }

    public static String getUsername(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("username").asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    public static String sign(String username, String secret) {
        try {
            Date date = new Date(System.currentTimeMillis()+EXPIRE_TIME);
            Algorithm algorithm = Algorithm.HMAC256(secret);
            // 附带username信息
            return JWT.create()
                    .withClaim("username", username)
                    .withExpiresAt(date)
                    .sign(algorithm);
        } catch (JWTCreationException e){
            return null;
        }

    }
}

之后在Controller中写登陆方法,
Shiro原本的登录验证是靠往官方的UsernamePasswordToken传入用户名密码来进行认证,
之后可以再经过自己自定义的一些校验规则来进行校验。
JWT的话不需要走Shiro的登陆流程,校验规则就没用Shiro的方法来配置,此处仅做演示。
通过了校验后再直接签发Token返回给客户端,并将token保存在WebStorage中(最好配置HTTPS进行传输)

@RestController
public class LoginController extends BaseController {
    @Resource
    private UserIService userService;

    @PostMapping("/login")
    public JsonResult login(@RequestParam("username") String username,
                            @RequestParam("password") String password){
        User user= userService.getUser(username);
        if (user.getPassword().equals(password)) {
            return new JsonResult(200, "Login success", JWTUtil.sign(username, password));
        } else {
            throw new UnauthorizedException();
        }
    }
}

接下来修改Shiro中进行验证的Realm类。
这里我们先重写验证方法中用到的AuthenticationToken中的方法来实现一个Token
(更具体的可以参照官方的UsernamePasswordToken)

public class JWTToken implements AuthenticationToken {

    // 密钥
    private String token;

    public JWTToken(String token) { this.token = token; }

    @Override
    public Object getPrincipal() { return token; }

    @Override
    public Object getCredentials() { return token; }

}

然后在Realm中修改对token的认证过程

public class ShiroRealm extends AuthorizingRealm {

    @Resource
    private UserIService userService;

    private static final Logger LOGGER = LogManager.getLogger(ShiroRealm.class);
    
    /**
     * 添加JWTToken支持
     */
    @Override
    public boolean supports(AuthenticationToken token){
        return token instanceof JWTToken;
    }

    /**
     * 登录认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
            throws AuthenticationException {
        String token = (String) authcToken.getPrincipal();
        String username = JWTUtil.getUsername(token);
        if (username == null) {
            throw new AuthenticationException("token invalid");
        }

        User user = userService.getUser(username);
        if (user == null) {
            throw new AuthenticationException("User didn't existed!");
        }

        if(!JWTUtil.verify(token, username, user.getPassword())){
            throw new TokenExpiredException("token invalid");
        }

        return new SimpleAuthenticationInfo(user, token, super.getName());
    }
}

目前为止登录部分是完成了。
剩下的需要配置请求过滤器进行JWT认证。

请求过滤

同样的,过滤器我们重写官方的BasicHttpAuthenticationFilter中的isLoginAttempt,executeLogin和isAccessAllowed方法
获取请求时放在Authorization请求头中的token信息来进行判断,调用Shiro的login方法走Realm的登陆验证流程

[此处应有代码]

之后进行Shiro的Config文件的配置。
将自定义的Filter配置在shiroFilter中,
同时关闭securityManager和SessionManager中的Session配置

[此处应有代码]

以上便是Shiro中结合JWT的一种配置方法。

总结

目前的这种JWT鉴权,还有一些其他细节上的问题:

例如要想进行Token自动刷新,每次请求都签发新的Token的话,之前的Token就应该销毁,
但由于服务端并没有状态,无法主动销毁,所以建议搭配Redis一类的缓存库,将Token单独抽离出来进行管理。

另外面对高并发的场景,会出现短时间内有多个请求都使用同一个Token的情况,
而第一个请求结束时就将之前的Token销毁了,导致验证出现了问题,
这种可以在签发Token时配置上宽裕时间(Leeway)来解决。(即允许让过期了一定时间内的Token依然可以通过验证)

 Vue自定义组件中嵌套循环时子组件的更新问题
Vue SPA项目加载优化 
上一篇:Vue自定义组件中嵌套循环时子组件的更新问题
下一篇:Vue SPA项目加载优化


已有 6 条评论


  1. nwrh
    nwrh

    单号购买 快递购买 空包代发www.uudanhaowang.com

     Reply
  2. bxll
    bxll

    第一单号网提供快递单号、淘宝快递单号、拼多多单号www.01kd.com

     Reply
  3. eghd
    eghd

    买单号,上单号网www.danhw.com

     Reply
  4. grthb
    grthb

    真实单号、快递代发www.kuaidzj.com

     Reply
  5. nbv
    nbv

    免费单号购买 不降权单号www.uudanhaowang.com

     Reply
  6. ewqr
    ewqr

    免费试用快递单号 免费试用空包单号网试用www.uudanhaowang.com

     Reply

如果我的文章对你有帮助,或许可以打赏一下呀!

支付宝
微信
QQ