JWT-Shiro 整合

JWT-与Shiro整合进行授权认证的大致思路 图示

大致思路

  1. 将登录验证从shiro中分离,自己结合JWT实现
  2. 用户登陆后请求认证服务器进行密码等身份信息确认,确认成功后 封装相关用户信息 生成token 相应给前端.
  3. 之后每次访问资源接口都在请求头中携带认证时生成的token
  4. 当发起资源请求时首先请求被请求过滤器拦截,拦截后判断请求头中是否含有token
  5. 如果含有token对token进行认证认证成功后对token进行解析,之后进行授权,拥有权限则进行放行
  6. 反之返回相关错误信息

核心点

  • token相关工具类的封装
  • 自定义重写shiro过滤器 extends AccessControlFilter
  • 自定义实现shiro Realm extends AuthorizingRealm
  • 实现自定义的shiroToken implements AuthenticationToken

具体代码

生成token的工具类

public class JWTUtils {
//密钥用于生成token的签名
private static final String SIGN = "!1qaz.("; /**
* 生成token
*/
public static String getToken(String userId,String userName, String roles, String permissions) {
Calendar instance = Calendar.getInstance();
instance.add(Calendar.DATE, 7);
JWTCreator.Builder builder = JWT.create()
.withIssuer("HuangShen")//token签发者
.withExpiresAt(instance.getTime()) //过期时间
.withClaim("userId", userId)//相关信息
.withClaim("userName", userName)
.withClaim("roles", roles)
.withClaim("permissions", permissions); //使用HMAC256算法生成token
String token = builder.sign(Algorithm.HMAC256(SIGN));
return token;
} /**
* 解码token
*
* @param token token
*/
public static DecodedJWT verify(String token){
DecodedJWT verify =JWT.require(Algorithm.HMAC256(SIGN)).build().verify(token);
return verify;
}

自定义重写shiro过滤器

public class JWTFilter extends AccessControlFilter {

    /**
* 此方法首先执行当此方法返回false时继续执行onAccessDenied方法
* 返回true允许访问
* 返回 false拒绝访问
*/
@Override
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception { //获取主体对象
Subject subject = SecurityUtils.getSubject();
System.out.println("===允许访问===");
//当主体对象不为空且已经获得认证时允许访问
if (null != subject && subject.isAuthenticated()){
return true;
}
return false;
} /**
* 当isAccessAllowed返回值为false时执行
*/
@Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String token = httpServletRequest.getHeader("token");
//客户端没有携带token
if (StringUtils.isEmpty(token)) {
System.out.println("请求头没有token");
return true;
}
System.out.println("拒接访问");
JWTToken jwtToken = new JWTToken(token);
Subject subject = SecurityUtils.getSubject();
//进行认证
subject.login(jwtToken);
return true;
}

自定义实现shiro Realm

/**
* 继承AuthorizingRealm类重写doGetAuthorizationInfo(授权)
* doGetAuthenticationInfo(认证)
*/
public class CustomerRealm extends AuthorizingRealm { /**
* 认证
* @param authenticationToken 认证token
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取主体信息
JWTToken principal = (JWTToken) authenticationToken;
DecodedJWT verify;
//创建自定义principal并赋值
TokenPayload tokenPayload = new TokenPayload();
//解析token
try {
verify = JWTUtils.verify((String) principal.getPrincipal());
tokenPayload.setUserId(verify.getClaim("userId").asString());
tokenPayload.setRoles(verify.getClaim("roles").asString());
tokenPayload.setUserName(verify.getClaim("userName").asString());
tokenPayload.setPermissions(verify.getClaim("permissions").asString());
} catch (AlgorithmMismatchException exception) {
throw new AuthenticationException("算法不匹配异常" + exception.getMessage());
} catch (SignatureVerificationException exception) {
throw new AuthenticationException("签名验证异常" + exception.getMessage());
} catch (TokenExpiredException exception) {
throw new AuthenticationException("token过期异常" + exception.getMessage());
} catch (InvalidClaimException exception) {
throw new AuthenticationException("无效Claim异常" + exception.getMessage());
} catch (JWTDecodeException exception) {
throw new AuthenticationException("JWT解码异常" + exception.getMessage());
} catch (JWTVerificationException exception) {
throw new AuthenticationException("JWT验证异常" + exception.getMessage());
} catch (RuntimeException exception) {
throw new RuntimeException(exception.getMessage());
} System.out.println("认证完成");
//将token解析过后的信息封装成为主体传入 授权时使用
return new SimpleAuthenticationInfo(tokenPayload, true, this.getName());
} /**
* 授权
* @param principalCollection 授权主体
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("开始授权");
TokenPayload primaryPrincipal = (TokenPayload) principalCollection.getPrimaryPrincipal(); System.out.println(primaryPrincipal.getRoles());
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//添加角色信息
simpleAuthorizationInfo.addRole(primaryPrincipal.getRoles());
//添加权限信息
simpleAuthorizationInfo.addStringPermission(primaryPrincipal.getPermissions()); System.out.println("授权完成");
return simpleAuthorizationInfo;
} @Override
public Class<?> getAuthenticationTokenClass() {
return JWTToken.class;
}

实现自定义的shiroToken

/**
* 为了便于使用由JWT生成的token 自定义实现自己的token
*/
public class JWTToken implements AuthenticationToken { //存储由请求头中获取的token
private final String jwtToken; public JWTToken(String jwtToken) {
this.jwtToken = jwtToken;
} @Override
public Object getPrincipal() {
return this.jwtToken;
} @Override
public Object getCredentials() {
return true;
}
}

shiro配置

@Configuration
public class ShiroConfig { /**
* 1.创建shiroFilterFactoryBean
* 负责拦截多有请求
*
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//给filter设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager); LinkedHashMap<String, Filter> filters = new LinkedHashMap<>();
filters.put("jwtfilter",new JWTFilter());
//将自定义过滤器加入到shiro 过滤其中
shiroFilterFactoryBean.setFilters(filters); // 完全无状态认证 noSessionCreation 不保留每次会话的session
//因此每次请求都会进行授权和认证
HashMap<String, String> map = new HashMap<>();
map.put("/shiro/login","anon");
map.put("/shiro/**","noSessionCreation,jwtfilter"); shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
shiroFilterFactoryBean.setLoginUrl("/shiro/unauthorized");
return shiroFilterFactoryBean;
} /**
* 2.创建安全管理器
*
* @param realm
* @return
*/
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("myRealm") Realm realm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
} /**
* 3.自定义Realm
*
* CredentialsMatcher 证书匹配器
* @return 自定义Realm
*/
@Bean("myRealm")
public Realm getRealm() {
CustomerRealm customerRealm = new CustomerRealm(); return customerRealm;
}

总结

使用JWTToken与shiro进无状态授权认证时 实际上登录时放弃的使用shiro的认证 登陆时使用自己实现的登录方法并且生成Token,无状态会话,用于shiro不存储每次会话的session 因此每次请求都会进行一次完整的shiro授权认证流程 ,可以使用Redis 等其他缓存的方式实现shiro的缓存 减小系统压力。

Shiro-JWT SpringBoot前后端分离权限认证的一种思路的更多相关文章

  1. Spring Security + JWT实现前后端分离权限认证

    现在国内前后端很多公司都在使用前后端分离的开发方式,虽然也有很多人并不赞同前后端分离,比如以下这篇博客就很有意思: https://www.aliyun.com/jiaocheng/650661.ht ...

  2. springSecurity + jwt + redis 前后端分离用户认证和授权

    记录一下使用springSecurity搭建用户认证和授权的代码... 技术栈使用springSecurity + redis + JWT + mybatisPlus 部分代码来自:https://b ...

  3. SpringBoot使用SpringSecurity搭建基于非对称加密的JWT及前后端分离的搭建

    SpringBoot使用SpringSecurity搭建基于非对称加密的JWT及前后端分离的搭建 - lhc0512的博客 - CSDN博客 https://blog.csdn.net/lhc0512 ...

  4. 从零玩转SpringSecurity+JWT整合前后端分离

    从零玩转SpringSecurity+JWT整合前后端分离 2021年4月9日 · 预计阅读时间: 50 分钟 一.什么是Jwt? Json web token (JWT), 是为了在网络应用环境间传 ...

  5. vue+springboot前后端分离实现单点登录跨域问题处理

    最近在做一个后台管理系统,前端是用时下火热的vue.js,后台是基于springboot的.因为后台系统没有登录功能,但是公司要求统一登录,登录认证统一使用.net项目组的认证系统.那就意味着做单点登 ...

  6. docker-compose 部署 Vue+SpringBoot 前后端分离项目

    一.前言 本文将通过docker-compose来部署前端Vue项目到Nginx中,和运行后端SpringBoot项目 服务器基本环境: CentOS7.3 Dokcer MySQL 二.docker ...

  7. 基于SpringBoot前后端分离的点餐系统

    基于SpringBoot前后端分离的点餐系统 开发环境:主要采用Spring boot框架和小程序开发 项目简介:点餐系统,分成卖家端和买家端.买家端使用微信小程序开发,实现扫码点餐.浏览菜单.下单. ...

  8. Springboot前后端分离开发

    .1.springboot前后端分离开发之前要配置好很多东西,这周会详细补充博客内容和遇到的问题的解析 2,按照下面流程走一遍 此时会加载稍等一下 pom.xml显示中加上阿里云镜像可以加速下载配置文 ...

  9. 实战!spring Boot security+JWT 前后端分离架构认证登录!

    大家好,我是不才陈某~ 认证.授权是实战项目中必不可少的部分,而Spring Security则将作为首选安全组件,因此陈某新开了 <Spring Security 进阶> 这个专栏,写一 ...

随机推荐

  1. 【Azure 环境】基于Azure搭建企业级内部站点, 配置私有域名访问的详细教程 (含演示动画)

    前言 在Azure中,可以通过App Service快速部署,构建自定义站点(PaaS服务).默认情况下,这些站点被访问URL都是面向公网,通过公网进行解析.为了最好的安全保障,是否可以有一种功能实现 ...

  2. LightningChart JS 3.0 新功能上线

    在这次的LC JS更新中,首次将极坐标图引入图表库. 这种全新的图表类型可以通过API轻松地进行样式设置.极坐标可以用作独立图表或在仪表板中使用. 另外,用于 XY图表的对数轴也添加到了这次的更新,L ...

  3. OO随笔之追求完美的第三单元——初试JML

    前言 这一章的JML比较简单,那么大家的关注点自然地移到了性能优化上.于是大家一股脑地去利用各种数据结构去做时间上的优化(当然很多人最后还是倒在了正确性上),故称追求完美的一单元.当然这也是得益于JM ...

  4. debian系统搭建telnet服务器以及telnet远程登陆--加油

    1.安装软件 sudo apt-get install telnet* 2.创建telnet文件 vim /etc/xinetd.d/telnet telnet内容 1 service telnet ...

  5. [bug] springboot 静态资源 layui.css 404

    目录结构 引用路径 <link rel="stylesheet" href="../static/layui/css/layui.css" type=&q ...

  6. [bug] Maven每次都自动下载jar包非常慢

    解决 方法一:将maven改为离线模式,自己下载jar包复制到仓库中 eclipse中Window>preferences>maven>勾选Offline 方法二:将maven镜像改 ...

  7. 再议GCC编译时的静态库依赖顺序问题

    相关博文1:http://blog.chinaunix.net/uid-20682147-id-76330.html相关博文:http://blog.chinaunix.net/uid-2068214 ...

  8. QT发布 - 动态编译,删减以来dll

    经常看到网上有些论调说 Qt 程序无比庞大,甚至拿 .NET 程序来比,说 Qt 程序打包以后跟 .NET 安装包差不多大.由此影响了很多人对 Qt 的选择.我觉得有必要对此做一些澄清-- 显然这个说 ...

  9. Debian 9.4 多网卡链路聚合bond配置

    Debian 9.4 多网卡链路聚合bond配置 安装ifenslave ifenslave 的作用是网卡的负载均衡 root@debian:~# apt-get install ifenslave ...

  10. 攻防世界(十一)warmup

    攻防世界系列 :warmup 1.打开题目,一个贱贱的滑稽表情 F12看到注释内容source.php 2.访问source.php <?php highlight_file(__FILE__) ...