Apache Shiro是一个功能强大、灵活的,开源的安全框架。它可以干净利落地处理身份验证、授权、企业会话管理和加密。越来越多的企业使用Shiro作为项目的安全框架,保证项目的平稳运行。

1.数据库表

2.pom依赖

    <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.16</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--shiro和spring整合--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.3.2</version></dependency><!--shiro核心包--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.3.2</version></dependency><!--shiro与redis整合--><dependency><groupId>org.crazycake</groupId><artifactId>shiro-redis</artifactId><version>3.0.0</version></dependency></dependencies>

3.自定义Realm

Realm域:Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源

public class CustomRealm extends AuthorizingRealm {
​@Overridepublic void setName(String name) {super.setName("customRealm");}
​@Autowiredprivate UserService userService;
​/*** 授权方法* 操作的时候,判断用户是否具有响应的权限* 先认证 -- 安全数据* 再授权 -- 根据安全数据获取用户具有的所有操作权限*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {//1.获取已认证的用户数据User user = (User) principalCollection.getPrimaryPrincipal();//得到唯一的安全数据//2.根据用户数据获取用户的权限信息(所有角色,所有权限)SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();Set<String> roles = new HashSet<>();//所有角色Set<String> perms = new HashSet<>();//所有权限for (Role role : user.getRoles()) {roles.add(role.getName());for (Permission perm : role.getPermissions()) {perms.add(perm.getCode());}}info.setStringPermissions(perms);info.setRoles(roles);return info;}
​
​/*** 认证方法* 参数:传递的用户名密码*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//1.获取登录的用户名密码(token)UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken;String username = upToken.getUsername();String password = new String(upToken.getPassword());//2.根据用户名查询数据库User user = userService.findByName(username);//3.判断用户是否存在或者密码是否一致if (user != null && user.getPassword().equals(password)) {//4.如果一致返回安全数据//构造方法:安全数据,密码,realm域名return new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());}//5.不一致,返回null(抛出异常)return null;}
​
}

4.Shiro的配置

SecurityManager 是 Shiro 架构的心脏,用于协调内部的多个组件完成全部认证授权的过程。例如通过调用realm完成认证与登录。使用基于springboot的配置方式完成SecurityManager,Realm的装配。

@Configuration
public class ShiroConfiguration {
​//1.创建realm@Beanpublic CustomRealm getRealm() {return new CustomRealm();}
​//2.创建安全管理器@Beanpublic SecurityManager getSecurityManager(CustomRealm realm) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(realm);
​//将自定义的会话管理器注册到安全管理器中securityManager.setSessionManager(sessionManager());//将自定义的redis缓存管理器注册到安全管理器中securityManager.setCacheManager(cacheManager());
​return securityManager;}
​//3.配置shiro的过滤器工厂/*** 在web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制*/@Beanpublic ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {//1.创建过滤器工厂ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();//2.设置安全管理器filterFactory.setSecurityManager(securityManager);//3.通用配置(跳转登录页面,为授权跳转的页面)filterFactory.setLoginUrl("/autherror?code=1");//跳转url地址filterFactory.setUnauthorizedUrl("/autherror?code=2");//未授权的url//4.设置过滤器集合
​/*** 设置所有的过滤器:有顺序map*     key = 拦截的url地址*     value = 过滤器类型**/Map<String, String> filterMap = new LinkedHashMap<>();//filterMap.put("/user/home","anon");//当前请求地址可以匿名访问
​//具有某中权限才能访问//使用过滤器的形式配置请求地址的依赖权限//filterMap.put("/user/home","perms[user-home]"); //不具备指定的权限,跳转到setUnauthorizedUrl地址
​//使用过滤器的形式配置请求地址的依赖角色//filterMap.put("/user/home","roles[系统管理员]");
​filterMap.put("/user/**", "authc");//当前请求地址必须认证之后可以访问
​filterFactory.setFilterChainDefinitionMap(filterMap);
​return filterFactory;}
​/*** 开启对shiro注解的支持*/@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();advisor.setSecurityManager(securityManager);return advisor;}
}

5.Shiro中的过滤器

注意:anon, authc, authcBasic, user 是第一组认证过滤器,perms, port, rest, roles, ssl 是第二组授权过滤器,要通过授权过滤器,就先要完成登陆认证操作(即先要完成认证才能前去寻找授权) 才能走第二组授权器(例如访问需要 roles 权限的 url,如果还没有登陆的话,会直接跳转到shiroFilterFactoryBean.setLoginUrl()设置的 url )

6.授权

(1)基于配置的授权

//配置请求连接过滤器配置
//匿名访问(所有人员可以使用)
filterMap.put("/user/home", "anon");
//具有指定权限访问
filterMap.put("/user/find", "perms[user-find]");
//认证之后访问(登录之后可以访问)
filterMap.put("/user/**", "authc");
//具有指定角色可以访问
filterMap.put("/user/**", "roles[系统管理员]");

(2)基于注解的授权

配置到方法上,表明执行此方法必须具有指定的权限

//查询
@RequiresPermissions(value = "user-find") public String find() {
return "查询用户成功"; }

配置到方法上,表明执行此方法必须具有指定的角色

//查询
@RequiresRoles(value = "系统管理员") public String find() {
return "查询用户成功"; }

7.统一异常处理

基于注解的配置方式进行授权,一旦操作用户不具备操作权限,目标方法不会被执行,而且会抛出AuthorizationException 异常。所以需要做好统一异常处理完成未授权处理

/*** 自定义的公共异常处理器*      1.声明异常处理器*      2.对异常统一处理*/
@ControllerAdvice
public class BaseExceptionHandler {
​@ExceptionHandler(value = AuthorizationException.class)@ResponseBodypublic String error(HttpServletRequest request, HttpServletResponse response,AuthorizationException e) {return "未授权";}
}

8.会话管理

在shiro里所有的用户的会话信息都会由Shiro来进行控制,shiro提供的会话可以用于JavaSE/JavaEE环境,不依赖于任何底层容器,可以独立使用,是完整的会话模块。通过Shiro的会话管理器(SessionManager)进行统一的会话管理。

(1)什么是shiro的会话管理

SessionManager(会话管理器):管理所有Subject的session包括创建、维护、删除、失效、验证等工作。

SessionManager是顶层组件,由SecurityManager管理 shiro提供了三个默认实现:

  1. DefaultSessionManager:用于JavaSE环境

  2. ServletContainerSessionManager:用于Web环境,直接使用servlet容器的会话。

  3. DefaultWebSessionManager:用于web环境,自己维护会话(自己维护着会话,直接废弃了Servlet容器的会话管理)。

(2)应用场景

在分布式系统或者微服务架构下,都是通过统一的认证中心进行用户认证。如果使用默认会话管理,用户信息只会保存到一台服务器上。那么其他服务就需要进行会话的同步。

(3)Shiro结合redis的统一会话管理

自定义shiro会话管理器

/*** 自定义的sessionManager*/
public class CustomSessionManager extends DefaultWebSessionManager {
​
​/*** 头信息中具有sessionid*      请求头:Authorization: sessionid** 指定sessionId的获取方式*/@Overrideprotected Serializable getSessionId(ServletRequest request, ServletResponse response) {
​//获取请求头Authorization中的数据String id = WebUtils.toHttp(request).getHeader("Authorization");if(StringUtils.isEmpty(id)) {//如果没有携带,生成新的sessionIdreturn super.getSessionId(request,response);}else{//返回sessionId;request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header");request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);return id;}}
}

配置Shiro基于redis的会话管理

@Configuration
public class ShiroConfiguration {  @Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private int port;
​/*** 1.redis的控制器,操作redis*/public RedisManager redisManager() {RedisManager redisManager = new RedisManager();redisManager.setHost(host);redisManager.setPort(port);return redisManager;}
​/*** 2.sessionDao*/public RedisSessionDAO redisSessionDAO() {RedisSessionDAO sessionDAO = new RedisSessionDAO();sessionDAO.setRedisManager(redisManager());return sessionDAO;}
​/*** 3.会话管理器*/public DefaultWebSessionManager sessionManager() {CustomSessionManager sessionManager = new CustomSessionManager();sessionManager.setSessionDAO(redisSessionDAO());return sessionManager;}
​/*** 4.缓存管理器*/public RedisCacheManager cacheManager() {RedisCacheManager redisCacheManager = new RedisCacheManager();redisCacheManager.setRedisManager(redisManager());return redisCacheManager;}
}

9.测试方法

整体架构为SpringBoot+SpringMVC+Spring Data Jpa

service,dao,entity已省略,controller层代码如下:

@RestController
public class UserController {
​@Autowiredprivate UserService userService;
​//个人主页//使用shiro注解鉴权//@RequiresPermissions()  -- 访问此方法必须具备的权限//@RequiresRoles() -- 访问此方法必须具备的角色
​/*** 1.过滤器:如果权限信息不匹配setUnauthorizedUrl地址* 2.注解:如果权限信息不匹配,抛出异常*/@RequiresPermissions("user-home")@RequestMapping(value = "/user/home")public String home() {return "访问个人主页成功";}
​//添加@RequestMapping(value = "/user",method = RequestMethod.POST)public String add() {return "添加用户成功";}//查询@RequestMapping(value = "/user",method = RequestMethod.GET)public String find() {return "查询用户成功";}//更新@RequestMapping(value = "/user/{id}",method = RequestMethod.GET)public String update(String id) {return "更新用户成功";}//删除@RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE)public String delete() {return "删除用户成功";}
​/***  1.传统登录*      前端发送登录请求 => 接口部分获取用户名密码 => 程序员在接口部分手动控制*  2.shiro登录*      前端发送登录请求 => 接口部分获取用户名密码 => 通过subject.login =>  realm域的认证方法**///用户登录@RequestMapping(value="/login")public String login(String username,String password) {//构造登录令牌try {
​/*** 密码加密:*     shiro提供的md5加密*     Md5Hash:*      参数一:加密的内容*              111111   --- abcd*      参数二:盐(加密的混淆字符串)(用户登录的用户名)*              111111+混淆字符串*      参数三:加密次数**/password = new Md5Hash(password,username,3).toString();
​UsernamePasswordToken upToken = new UsernamePasswordToken(username,password);//1.获取subjectSubject subject = SecurityUtils.getSubject();
​//获取sessionString sid = (String) subject.getSession().getId();
​//2.调用subject进行登录subject.login(upToken);return "登录成功";}catch (Exception e) {return "用户名或密码错误";}}
}

配置文件如下:

server:port: 8081
spring:application:name: shiro-test #指定服务名datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/shiro_db?useUnicode=true&characterEncoding=utf8username: rootpassword: rootjpa:database: MySQLshow-sql: trueopen-in-view: trueredis:host: 127.0.0.1port: 6379

Shiro在SpringBoot中的应用相关推荐

  1. [Shiro] - Shiro之SpringBoot中的使用

    下载了运行项目后,访问路径:http://localhost/shiro/login 这篇应该在进阶后面的. shiro中的重中之重,一定要看. 基于springboot+thymeleaf+shir ...

  2. Shiro的在Springboot中的使用

    一.Shiro简介 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码和会话管理.使用Shiro的易于理解的API,您可以快速.轻松地获得任何应用程序,从最小的移动应 ...

  3. springBoot中shiro与Redis整合的配置文件

                                                                 springBoot中shiro与Redis整合的配置文件 整合依赖: < ...

  4. 在SpringBoot中使用Spring Session解决分布式会话共享问题

    在SpringBoot中使用Spring Session解决分布式会话共享问题 问题描述: 每次当重启服务器时,都会导致会员平台中已登录的用户掉线.这是因为每个用户的会话信息及状态都是由session ...

  5. 难以想象SpringBoot中的条件注解底层居然是这样实现的

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源 | https://urlify.cn/bm2qqi Spr ...

  6. 在springboot中使用springsecurity实现安全控制

    SpringSecurity官方文档 我们在编写web应用时经常要对一些页面做安全控制,比如:对于没有访问权限的用户需要转到登录表单页面.要实现访问控制方法的多种多样,可以通过AOP.拦截器实现,也可 ...

  7. Shiro 整合 SpringBoot

    Shiro 整合 SpringBoot shiro主要有三大功能模块 Subject:主体,一般指用户. SecurityManager:安全管理器,管理所有Subject,可以配合内部安全组件.(类 ...

  8. java参数值注入_在springboot中使用注解将值注入参数的操作

    后端的许多管理系统需要登陆者的信息,如shiro登陆后,会将登陆者的信息存储在shiro的session,在使用时需要多行代码获取用户信息.可以把获取在shiro中的登陆者信息封装在一个类中,使用时获 ...

  9. java quartz管理,SpringBoot中使用Quartz管理定时任务的方法

    定时任务在系统中用到的地方很多,例如每晚凌晨的数据备份,每小时获取第三方平台的 Token 信息等等,之前我们都是在项目中规定这个定时任务什么时候启动,到时间了便会自己启动,那么我们想要停止这个定时任 ...

最新文章

  1. CSS布局代码:两列布局实例
  2. 18春《c语言》在线作业3,18春福师《C++语言程序设计》在线作业二【参考答案】...
  3. 国内HuggingFace,预训练模型镜像使用
  4. java 中for循环中重复定义的变量 为什么不报错?
  5. 移位运算符:,,总结
  6. Netty学习总结(5)——Netty之TCP粘包/拆包问题的解决之道
  7. php passport security,php写的Passport加密函数
  8. 01-信贷路由项目架构和 rose 框架的搭建
  9. C语言题目——三子棋游戏
  10. 【电子元件】稳压(齐纳)管 Zener Diode
  11. 零基础自学SQL课程 | UNION 联合查询
  12. h5页面 请在微信客户端打开链接_模拟微信接口时,提示“请在微信客户端打开链接”(转)...
  13. Java整型变量举例_java 整型常量和整型变量的问题
  14. 重磅 I IT4IT 2.1中文版正式发布特邀专家彭斐推荐
  15. 软件实习项目2——贪吃喵(猫吃鱼版贪吃蛇)(成品展示)
  16. 三种方法教你让模糊照片秒变高清图
  17. 电脑wifi通过中继器上网频繁掉线问题分析及问题解决方案(DHCP NAK)
  18. 纯css画一个樱桃小丸子
  19. 基于VSG的并网逆变器设计
  20. matlab在c盘有缓存文件夹吗,上网看电影时,文件是缓存在C盘的什么文件夹里?

热门文章

  1. 能量分析攻击day02
  2. 51. N 皇后(回溯算法)
  3. 在ubuntu20.10上搭建SVN Server
  4. optee中core_init_mmu_regs函数解读
  5. 【Web安全】Web开发中常见的安全误区
  6. MySQL ORDER BY:对查询结果进行排序
  7. 1143 Lowest Common Ancestor (30 分)【难度: 中 / 知识点: 最低公共祖先 未完成】
  8. 1018 Public Bike Management (30 分) 【难度: 难 / 知识点: 图论 最短路 图的遍历】
  9. 【C++】运算符重载
  10. 3.1.11 段页式管理方式