文章目录
一、shiro 简介
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
Shiro可以完成认证、授权、加密、会话管理、web集成、缓存等。
1、shiro の基本功能
Shiro不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。Shiro 可以实现认证、授权、加密、会话管理、与 Web 集成、缓存等。shiro基本功能点如下图所示:
Authentication:身份认证
/ 登录,验证用户是不是拥有相应的角色。
Authorization:授权
,即权限验证,验证某个已认证/已登录
的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限。
Session Management:会话管理
,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的。
Cryptography:加密
,保护数据的安全性,如密码加密存储到数据库,而不是明文存储。
Web Support:Web 支持
,可以非常容易的集成到 Web 环境。
Caching:缓存
,比如用户登录后,其用户信息、拥有的角色 / 权限不必每次去查,这样可以提高效率。
Concurrency:shiro 支持多线程应用的并发
验证,即如在一个线程中开启另一个线程,能把权限自动传播过去。
Testing:提供测试
支持。
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问。
Remember Me:记住我
,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。
Shiro 不会去维护用户、维护权限;这些需要我们自己去设计 / 提供;然后通过相应的接口注入给 Shiro 即可。
2、shiro外部の系统架构
三个核心组件:Subject, SecurityManager 和 Realms
。
Subject:本质上就是当前访问用户的抽象描述。
SecurityManager:是Shiro架构中最核心的组件,通过它可以协调其他组件完成用户认证和授权。实际上,SecurityManager就是Shiro框架的控制器。
Realm:定义了访问数据的方式,用来连接不同的数据源,如:LDAP,关系数据库,配置文件等等。
对于一个简单 Shiro 应用:应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager;我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。从以上也可以看出,Shiro 不提供维护用户 / 权限,而是通过 Realm 让开发人员自己注入,当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权
。配置多个Realm是可以的,但是至少需要一个。
3、shiro内部の系统架构
Subject:主体,可以看到主体可以是任何可以与应用交互的 “用户”。
SecurityManager:相当于 SpringMVC 中的 DispatcherServlet ,是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。
Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了。
Authorizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能。
Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,或者内存实现等等;由用户提供;我们一般在应用中都需要实现自己的 Realm。
SessionManager: Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境、EJB 等环境;所以Shiro 就抽象了一个自己的 Session 来管理主体与应用之间交互的数据;这样的话,比如我们在 Web 环境用,刚开始是一台 Web 服务器;接着又上了台 EJB 服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到 Memcached 服务器)。
SessionDAO:DAO 大家都用过,数据访问对象,用于会话的 CRUD,比如我们想把 Session 保存到数据库,那么可以实现自己的 SessionDAO,通过如 JDBC 写到数据库;另外 SessionDAO 中可以使用 Cache 进行缓存,以提高性能。
CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能。
Cryptography:密码模块,Shiro 提供了一些常见的加密组件用于如密码加密 / 解密的。
二、springboot 整合 shiro 环境搭建
1、导入相の依赖
<!-- shiro整合spring的包(依赖)-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>
<!-- thymeleaf依赖-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
2、ShiroConfig配置类の搭建
package com.tiger.config;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author huxuehao
* @create 2021-08-17-21:51
*/
@Configuration
public class ShiroConfig {
//3、ShiroFilterFactoryBean
// @Qualifier("getDefaultWebSecurityManager") 表示当前类中的getDefaultWebSecurityManager
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
filterFactoryBean.setSecurityManager(securityManager);
此处可以添加shiro的内置过滤器...
return filterFactoryBean;
}
//2、DefaultWebSecurityManager
// @Qualifier("userRealm") 表示当前类中的 userRealm 方法
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm
securityManager.setRealm(userRealm);
return securityManager;
}
//1、创建 realm对象,自定义
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}
3、Realm继承类の搭建
package com.tiger.config;
import org.apache.shiro.realm.AuthorizingRealm;
public class UserRealm extends AuthenticatingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("【execute】: doGetAuthorizationInfo be executed ...");
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)throws AuthenticationException {
System.out.println("【execute】: doGetAuthenticationInfo be executed ...");
return null;
}
}
4、前端页面の搭建
我们需要以下一个前端页面(使用的是thymeleaf模板引擎),这个大家在自己写就可以
5、Controller控制器の搭建
关于后台的路由Controller,如下:
package com.tiger.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class routeController {
@RequestMapping("/admin/main")
public String admin(){
return "pages/admin/adminPage";
}
@RequestMapping("/user/main")
public String user(){
return "pages/user/userPage";
}
@RequestMapping("/tourist/main")
public String tourist(){
return "pages/tourist/touristPage";
}
@RequestMapping("/login")
public String login(){
return "pages/loginPage";
}
}
三、shiro拦截与认证
我们在开发网站的时候,往往会有如下的需求:对于不同的页面,用户用于不同的角色才能访问,一个页面只有登录之后用户才能访问。
1、实现拦截
在shiro中的拦截,是在ShiroConfig配置类中实现的,至于拦截的相关操作我们都写在ShiroConfig配置类中的getShiroFilterFactoryBean()方法中。shiro中的拦截(认证)级别:
anno : 无需认证即可访问
authc : 必须认证才能访问
user : 必须有"记住我"才能访问
perms : 用于对某个资源的权限才能访问
role : 必须拥有某个角色才能访问
//3、ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager securityManager){
//构造 ShiroFilterFactoryBean 对象
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
filterFactoryBean.setSecurityManager(securityManager);
//添加 shiro 的内置过滤器
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); //构造一个map
//"/user/*","authc" :表示访问/user/下的内容被设置的拦截(认证)级别为authc(必须认证才能访问)
filterChainDefinitionMap.put("/user/*","authc");
filterChainDefinitionMap.put("/admin/*","authc");
//设置过滤器链定义映射,用于实现上面的拦截设置
filterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
//当页面被拦截之后,跳到的登录页面的url的设置
filterFactoryBean.setLoginUrl("/login");
return filterFactoryBean;
}
2、实现认证
对于 shiro 而言,实现认证我们是在 Realm继承类 中的重写方法中实现的。我们目前还没有连接数据库(整合mybatis),所以现在先试用固定用户名和密码进行认证。
所谓的认证其实就是登录,既然需要登录,那么from表单就要有一个对应的目标路由,我们将shiro中接收username和password以及登录验证的功能写在了from表单对应的目标路由中
。
@RequestMapping("/goLogin")
public String goLogin(String username, String password, Model model){
//获取当前的用户
Subject subject = SecurityUtils.getSubject();
//封装用户数据生成令牌(是一种加密操作)
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
try {
//设置当前用户登录(登录验证)
subject.login(token);
//若用户名和密码都成功,那么访问index
return "index";
} catch (UnknownAccountException uae) {
model.addAttribute("msg","用户名不存在");
return "pages/loginPage";
} catch (IncorrectCredentialsException ice) {
model.addAttribute("msg","密码错误");
return "pages/loginPage";
} catch (LockedAccountException lae) {
model.addAttribute("msg","该账户暂时被锁定");
return "pages/loginPage";
}
catch (AuthenticationException ae) {
model.addAttribute("msg","身份认证异常,请重新登录");
return "pages/loginPage";
}
}
在上述的代码中有UsernamePasswordToken token = new UsernamePasswordToken(username,password)【代码1】
和subject.login(token)【代码2】
这个两段代码,其作用就是现实登录验证,登录时用于验证的用户名和密码就是使用的【代码1】中的username和password。
此时你会有一个疑问,我们并没有设置认证的规则,并不知道正确的用户名和密码,shiro是怎么进行验证的。其实shiro的认证是在 Realm继承类 中实现的
,下面我们重写 Realm继承类 中的doGetAuthenticationInfo方法,如下:
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {
System.out.println("【execute】: doGetAuthenticationInfo be executed ...");
//这个地方就是为什么上段代码和这段代码可以进行联动的原因
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//暂时使用:固定用户名和密码
String username = "admin";
String password = "000000";
//用户名认证:如果用户名不存在,那么上段代码会自动捕获异常并抛出异常
if (!token.getUsername().equals(username)){
return null;
}
//密码认证:密码认证shiro内部自己做,防止密码泄露。如果密码认证失败,那么上段代码会自动捕获异常并抛出异常
return new SimpleAuthenticationInfo("",password,"");
}
经过上述的两段代码,我们就实现了shiro红的认证功能。
3、测试
①、点击"进入管理员页面"会被拦截,因为未进行身份认证
②、进行"身份认证"
③、"身份认证"成功,不会被拦截
④、访问"进入管理员页面"成功
四、shiro整合mybatis+druid
上面我们进行认证的时候,使用的是固定的用户名和密码,但是实际开发中,用户的真是用户名和密码(应该进行加密处理)应该是从数据库中取出来了的。下面我们就来打通shiro与数据库的通道。
1、导入 mybatis 相关依赖
<!-- mybatis-spring -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!-- lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!-- jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
<!-- log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2、DruidConfig配置类の搭建
在这就不过多解释了,有疑惑的朋友可以看我前面写的【SpringBoot】SpringBoot中的Data(jdbc、druid、mybatis)相关整合。
package com.tiger.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import javax.sql.DataSource;
import java.util.HashMap;
@Configuration
public class DruidConfig {
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druidDataSource(){
return new DruidDataSource();
}
//后台监控功能
//因为springboot内置了servlet容器,所以没有web.xml。替代方法 ServletRegistrationBean
@Bean
public ServletRegistrationBean StatViewServlet(){
//固定写法
ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>();
bean.setServlet(new StatViewServlet());
bean.addUrlMappings("/druid/*");
//后台需要有人登陆
HashMap<String , String> initParameters = new HashMap<>();
//增加配置
initParameters.put("loginUsername","admin");
initParameters.put("loginPassword","123456");
//允许谁能访问
initParameters.put("allow",""); //("allow","localhost")只允许本机访问;("allow","")允许所有人访问;
//禁止谁访问 initParameters.put("tiger","192.168.0.1");
//设置初始化参数
bean.setInitParameters(initParameters);
return bean;
}
//Filter
@Bean
public FilterRegistrationBean b(){
//固定写法
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
bean.setFilter(new WebStatFilter());
//可以过滤那些请求
HashMap<String , String > initParameters = new HashMap<>();
initParameters.put("exclusions","*.js,*.css,/druid/*");
//设置初始化参数
bean.setInitParameters(initParameters);
return bean;
}
}
3、配置 druid の数据源以及其他配置(application.yaml)
spring:
datasource:
username: root
password: root123123
url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
# 下面为连接池的补充设置,应用到上面所有数据源中
# 初始化大小,最小,最大
initialSize: 5
minIdle: 5
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# 打开PSCache,并且指定每个连接上PSCache的大小
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的 Filter,去掉后监控界面 SQL 无法统计,wall 用于防火墙 日志 log4j
filters: stat,wall,log4j #导入了log4j
useGlobalDataSourceStat: true
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
4、mybatisの配置(application.yaml)
# 整合mybatis
mybatis:
type-aliases-package: com.tiger.pojo # 别名包(别名就是类名)
mapper-locations: classpath:mybatis/mapper/*.xml #mapper的位置
5、编写 mapper 以及 mapper.xml
因为在进行身份验证是时,我们一般会通过用户名查密码。
UserMapper:
package com.tiger.mapper;
import com.tiger.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface UserMapper {
User queryUserByName(String name);
}
UserMapper.xml:(resources/mybatis/mapper/UserMapper.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--绑定Mapper-->
<mapper namespace="com.tiger.mapper.UserMapper">
<select id="queryUserByName" resultType="User" parameterType="String">
select * from `user` where `name` = #{name}
</select>
</mapper>
6、service层の编写
UserService:
package com.tiger.service;
import com.tiger.pojo.User;
public interface UserService {
User queryUserByName(String name);
}
UserServiceImpl:
package com.tiger.service;
import com.tiger.mapper.UserMapper;
import com.tiger.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User queryUserByName(String name) {
return userMapper.queryUserByName(name);
}
}
7、重写 Realm 继承类中のdoGetAuthenticationInfo 方法
@Autowired
UserService userService;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {
System.out.println("【execute】: doGetAuthenticationInfo be executed ...");
//这个地方就是为什么上段代码和这段代码可以进行联动的原因
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//获取数据库信息
User user = userService.queryUserByName(token.getUsername());
//用户名认证:如果用户名不存在,那么上段代码会自动捕获异常并抛出异常
if (!token.getUsername().equals(user.getName())){
return null;
}
//密码认证:密码认证shiro内部自己做,防止密码泄露。如果密码认证失败,那么上段代码会自动捕获异常并抛出异常
return new SimpleAuthenticationInfo("",user.getPasswd(),"");
}
到此为止,我们是成功的打开了shiro与数据库的通道。
五、shiro 请求授权
我们可能想达到这样一个效果:对于"管理员页面",我们只希望管理员可以访问;对于"用户页面",我们只希望管理员和用户可以访问;对于"游客页面",我们希望管理员、用户、游客都可以访问,那么此时使用到请求授权了。
1、基于资源的权限管理
首先,我们需要给对应的资源加上权限管理,我们在ShiroConfig配置类
中的getShiroFilterFactoryBean()
实现
之前的拦截是通过如下代码实现的
//"/user/*","authc" :表示访问/user/下的内容被设置的拦截(认证)级别为authc(必须认证才能访问)
filterChainDefinitionMap.put("/user/*","authc");
filterChainDefinitionMap.put("/admin/*","authc");
而我们的基于资源的权限管理的资源权限是通过如下的代码实现的,perms依然具有authc的拦截功能
admin : * : * :admin 表示权限类型,第一个 * 表示操作类型,第一个 * 表示操作的具体条目,这样表示更加清晰,并不是强制要求。如:admin : delete : 01 表示admin具有删除01条目的权限,
perms[…]:中可以添加多个权限(用逗号分隔),当某个用户同时满足perms[...]中所有的权限,才可以访问
//拥有对某个资源的权限才能访问,通过授权才可以
filterChainDefinitionMap.put("/admin/*","perms[admin:*:*]");
filterChainDefinitionMap.put("/user/*","perms[user:*:*]");
filterChainDefinitionMap.put("/tourist/*","perms[tourist:*:*]");
至于资源的授权,我们是在Realm的实现类中的doGetAuthorizationInfo()
方法实现的
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("【execute】: doGetAuthorizationInfo be executed ...");
//获取AuthorizationInfo对象
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
//获取当前的用户
Subject currentUser = SecurityUtils.getSubject();
//从subject中获取Principal,强转成user
User user = (User)currentUser.getPrincipal();
//设置授权(user对应的权限从user对象中取出)
String[] authorities = user.getAuthority().split(",");//分割权限
for (String authority : authorities) {
//给当前用户添加相应的权限
authorizationInfo.addStringPermission(authority);
}
return authorizationInfo;
}
我们可以发现用户名为admin的用户的authority为admin:*:*,user:*:*,tourist:*:*
(如下图)经过split(",")分割之后,就可以得到admin:*:*
、user:*:*
、tourist:*:*
三条权限
从上面的代码中,我们可以发现有这么一段代码,如下
//获取当前的用户
Subject currentUser = SecurityUtils.getSubject();
//从subject中获取Principal,因为我们当时存的就是user,所以可以强转成user
User user = (User)currentUser.getPrincipal();
其作用是接收“认证”方法中返回过来的用户的对象(在认证方法中查询数据库得到的),如下
//将user对象保存到subject的principal属性中;
//密码认证shiro做,防止密码泄露
return new SimpleAuthenticationInfo(user, user.getPasswd(),"");
当然你也可以不这么操作,而是在doGetAuthorizationInfo()中再次访问数据库获取到用户的对象。
这样,我们就完成了基于资源的权限管理功能。
2、基于角色的权限管理
首先,我们需要给对应的资源加上基于角色的权限管理,我们在ShiroConfig配置类
中的getShiroFilterFactoryBean()
实现
roles[…]:中可以添加多个权限(用逗号分隔),当某个用户同时拥有roles[...]中所有的角色,才可以访问
//拥有对某个资源的权限才能访问,通过授权才可以
filterChainDefinitionMap.put("/admin/*","roles[admin]");
filterChainDefinitionMap.put("/user/*","roles[user]");
filterChainDefinitionMap.put("/tourist/*","roles[tourist]");
至于用户的角色添加,我们是在Realm的实现类
中的doGetAuthorizationInfo()
方法实现的
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("【execute】: doGetAuthorizationInfo be executed ...");
//获取AuthorizationInfo对象
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
//获取当前的用户
Subject currentUser = SecurityUtils.getSubject();
//从subject中获取Principal,强转成user
User user = (User)currentUser.getPrincipal();
//设置角色(对应的角色从数据库中取)
String[] roles = user.getRole().split(",");
for (String role : roles) {
authorizationInfo.addRole(role);
}
return authorizationInfo;
}
这样,我们就完成了基于角色的权限管理功能。
六、shiro 开启缓存
没有缓存前:
有缓存之后:
1、介绍
当我们每次访问被拦截的请求时,都会进行相关的认证和授权,又因为我们在认证和授权的过程中有数据库操作,这就导致计算机资源开销很大。为了避免上述的问题,我们可以使用缓存。
Shiro 提供了类似于 Spring 的 Cache 抽象,即 Shiro 本身不实现 Cache,但是对 Cache 进行了又抽象,方便更换不同的底层 Cache 实现。我们可以在shiro中设置CacheManager,以实现通过第三方缓存来实现shiro的缓存。
接下来,我们将ehcache注入到shiro的CacheManager中,通过ehcache实现shiro的缓存。
2、导入依赖
<!-- shiro-ehcache依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.7.1</version>
</dependency>
3、配置shiro中的缓存
配置shiro中的缓存我们是在ShiroConfig配置类中的userRealm()中进行配置的
我们需要设置shiro缓存管理器,因为shiro的缓存默认是关闭的,所以我们需要开启全局缓存,最后需要开启认证、授权缓存并设置缓存名,如下:
@Bean
public UserRealm userRealm(){
UserRealm userRealm = new UserRealm();
//设置缓存管理
userRealm.setCacheManager(new EhCacheManager());//设置shiro缓存管理器
userRealm.setCachingEnabled(true); //开启全局缓存
userRealm.setAuthenticationCachingEnabled(true); //开启认证缓存
userRealm.setAuthenticationCacheName("authenticationCache");
userRealm.setAuthorizationCachingEnabled(true); //开启授权缓存
userRealm.setAuthorizationCacheName("authorizationCache");
return userRealm;
}
至此,我们就开启了shiro中基于ehcache的缓存。
七、shiro 整合 thymeleaf
我们现在想要实现,当登录后,用户的角色是admin,那么"进入管理员页面"、“进入用户页面”、“进入游客员页面"都显示;用户的角色是user,那么"进入用户页面”、"进入游客员页面"被显示;用户的角色是tourist,只显示"进入游客员页面"都显示。
1、导入相关依赖
<!-- thymeleaf依赖-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
<!-- thymeleaf-shiro 整合依赖-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
2、引入头文件
在相关的HTML页面引入thymeleaf-shiro头文件
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
...
</html>
3、使用shiro标记
判断是否具有某些角色(role)权限,例如:shiro:hasRole=“admin” 的作用是:如果当前的用户具有 admin 角色,那么 shiro:hasRole=“admin” 所在的标签将会被渲染(生效)
<div class="box">
<h2>【主页】
<a th:href="@{/login}">登录</a>
<a th:href="@{/logout}">注销</a>
</h2>
<br>
<div shiro:hasRole="admin">
<a th:href="@{/admin/main}">进入管理员页面</a>
</div>
<br>
<div shiro:hasRole="user">
<a th:href="@{/user/main}">进入用户页面</a>
</div>
<br>
<div shiro:hasRole="tourist">
<a th:href="@{/tourist/main}">进入游客页面</a>
</div>
</div>
判断是否具有某些资源(perms)权限,例如:shiro:hasPermission="admin: * : * " 的作用是:如果当前的用户具有 admin: * : * 权限,那么 shiro:hasPermission="admin: * : * " 所在的标签将会被渲染(生效)
<div class="box">
<h2>【主页】
<a th:href="@{/login}">登录</a>
<a th:href="@{/logout}">注销</a>
</h2>
<br>
<div shiro:hasPermission="admin:*:*">-->
<a th:href="@{/admin/main}">进入管理员页面</a>
</div>
<br>
<div shiro:hasPermission="user:*:*">-->
<a th:href="@{/user/main}">进入用户页面</a>
</div>
<br>
<div shiro:hasPermission="tourist:*:*">-->
<a th:href="@{/tourist/main}">进入游客页面</a>
</div>
</div>
更新中,尽情期待…
八、实现注册(MD5 + salt 加密)与登录验证
更新中,尽情期待…
当一切都随风而逝的时候,那些特别的瞬间都成了永恒…