![34ef98ed6c4911ef7741fa40f9b7fb48.png](https://i-blog.csdnimg.cn/blog_migrate/46474c6f5800f7cba59d8a9af0146932.jpeg)
今天青秧想和和大家聊聊 Spring Boot 整合 Shiro 的话题!在 Spring Boot 中做权限管理,一般来说,主流的方案是 Spring Security ,从技术角度来说,也可以使用 Shiro。
pache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
Apache Shiro 体系结构 :
Authentication 认证 ---- 用户登录
Authorization 授权 — 用户具有哪些权限
Cryptography 安全数据加密
Session Management 会话管理
Web Integration web系统集成
Interations 集成其它应用,spring、缓存框架
一、SpringBoot整合Shiro
Shiro的核心API:
- Subject: 用户主体(把操作交给SecurityManager)
- SecurityManager:安全管理器(关联Realm)
- Realm:Shiro连接数据的桥梁
1.导入Shiro依赖:
<!-- shiro与spring整合依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
2.自定义Realm类:
/**
* 自定义Realm
* @author lenovo
*
*/
public class UserRealm extends AuthorizingRealm{
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
System.out.println("执行授权逻辑");
return null;
}
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
System.out.println("执行认证逻辑");
return null;
}
}
3.编写Shiro配置类:
/**
* Shiro的配置类
* @author lenovo
*
*/
@Configuration
public class ShiroConfig {
/**
* 创建ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
/**
* 创建DefaultWebSecurityManager
*/
@Bean(name="securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm
securityManager.setRealm(userRealm);
return securityManager;
}
/**
* 创建Realm
*/
@Bean(name="userRealm")
public UserRealm getRealm(){
return new UserRealm();
}
}
4.使用Shiro内置过滤器实现页面拦截:
/**
* Shiro的配置类
* @author lenovo
*
*/
@Configuration
public class ShiroConfig {
/**
* 创建ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//添加Shiro内置过滤器
/**
* Shiro内置过滤器,可以实现权限相关的拦截器
* 常用的过滤器:
* anon: 无需认证(登录)可以访问
* authc: 必须认证才可以访问
* user: 如果使用rememberMe的功能可以直接访问
* perms: 该资源必须得到资源权限才可以访问
* role: 该资源必须得到角色权限才可以访问
*/
Map<String,String> filterMap = new LinkedHashMap<String,String>();
/*filterMap.put("/add", "authc");
filterMap.put("/update", "authc");*/
filterMap.put("/testThymeleaf", "anon");
filterMap.put("/*", "authc");
//默认的登陆访问url
shiroFilterFactoryBean.setLoginUrl("/toLogin");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
/**
* 创建DefaultWebSecurityManager
*/
@Bean(name="securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm
securityManager.setRealm(userRealm);
return securityManager;
}
/**
* 创建Realm
*/
@Bean(name="userRealm")
public UserRealm getRealm(){
return new UserRealm();
}
}
注意:
Shiro内置过滤器,可以实现权限相关的拦截器
常用的过滤器:
anon: 无需认证(登录)可以访问。
authc: 必须认证才可以访问。
user: 如果使用rememberMe的功能可以直接访问。
perms: 该资源必须得到资源权限才可以访问。
role: 该资源必须得到角色权限才可以访问。
filterMap是一个底层以双向链表实现的LinkedHashMap,它存入是什么顺序,读取就是什么顺序,顺序从上到下,优先级依次降低。
5.实现用户认证(登录)操作:
控制器逻辑处理:
/**
* 登录逻辑处理
*/
@RequestMapping("/login")
public String login(String name,String password,Model model){
/**
* 使用Shiro编写认证操作
*/
//1.获取Subject
Subject subject = SecurityUtils.getSubject();
//2.封装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(name,password);
//3.执行登录方法
try {
subject.login(token);
//登录成功
//跳转到test.html
return "redirect:/testThymeleaf";
} catch (UnknownAccountException e) {
//e.printStackTrace();
//登录失败:用户名不存在
model.addAttribute("msg", "用户名不存在");
return "login";
}catch (IncorrectCredentialsException e) {
//e.printStackTrace();
//登录失败:密码错误
model.addAttribute("msg", "密码错误");
return "login";
}
}
编写Realm的判断逻辑:
/**
* 自定义Realm
* @author lenovo
*
*/
public class UserRealm extends AuthorizingRealm{
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
System.out.println("执行授权逻辑");
return null;
}
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
System.out.println("执行认证逻辑");
//假设数据库的用户名和密码
String name = "eric";
String password = "123456";
//编写shiro判断逻辑,判断用户名和密码
//1.判断用户名
UsernamePasswordToken token = (UsernamePasswordToken)arg0;
if(!token.getUsername().equals(name)){
//用户名不存在
return null;//shiro底层会抛出UnKnowAccountException
}
//2.判断密码
return new SimpleAuthenticationInfo("",password,"");
}
}
注意:
当前用户调用login()方法后,抛出UnknownAccountException异常,就说明用户名不存在,抛出IncorrectCredentialsException异常,就说明用户密码错误,若不出现异常,则登录验证成功。
只要调用了当前对象的login()方法,Shiro就会找到自定义的Realm,执行它的认证逻辑方法:doGetAuthenticationInfo(),所以我们可以在自定义Realm中进行一些相关的逻辑操作。
6.实现用户授权操作:
ShiroConfig配置类加上资源权限操作:
![b11bc1ca0ec4f86d8a33134dc22af896.png](https://i-blog.csdnimg.cn/blog_migrate/870744071d165c8f64f08c8a3e1c3de9.jpeg)
自定义Realm授权逻辑:
/**
* 自定义Realm
* @author lenovo
*
*/
public class UserRealm extends AuthorizingRealm{
/**
* 执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
System.out.println("执行授权逻辑");
//给资源进行授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//添加资源的授权字符串
//info.addStringPermission("user:add");
//到数据库查询当前登录用户的授权字符串
//获取当前登录用户
Subject subject = SecurityUtils.getSubject();
User user = (User)subject.getPrincipal();
User dbUser = userSerivce.findById(user.getId());
info.addStringPermission(dbUser.getPerms());
return info;
}
@Autowired
private UserService userSerivce;
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
System.out.println("执行认证逻辑");
//编写shiro判断逻辑,判断用户名和密码
//1.判断用户名
UsernamePasswordToken token = (UsernamePasswordToken)arg0;
User user = userSerivce.findByName(token.getUsername());
if(user==null){
//用户名不存在
return null;//shiro底层会抛出UnKnowAccountException
}
//2.判断密码
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
}
注意:
当shiro配置类中添加过滤器权限为perms时,Shiro就会找到自定义的Realm,执行它的授权逻辑方法:doGetAuthorizationInfo(),所以我们可以在自定义Realm中进行一些相关的逻辑操作。
执行授权逻辑方法之前,会先执行认证逻辑方法,先认证成功之后再判断当前用户是否有权限访问该资源。
认证逻辑方法:
![b97e4ee77ee26b2c70ee4d4b3ca51cbf.png](https://i-blog.csdnimg.cn/blog_migrate/ee6fe3edab8f525fcb1d6facfeea7369.png)
授权逻辑方法:
![86728da610f848ef008ff3acd12910af.png](https://i-blog.csdnimg.cn/blog_migrate/cc17d7f36046e428929347a594950151.jpeg)
7.thymeleaf和shiro标签整合使用
导入thymeleaf扩展依赖:
<!-- thymel对shiro的扩展坐标 -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
配置ShiroDialect,在ShiroConfig类里面添加getShiroDialect方法:
/**
* 配置ShiroDialect,用于thymeleaf和shiro标签配合使用
*/
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
在页面上使用shiro标签:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>测试Thymeleaf的使用</title>
</head>
<body>
<h3 th:text="${name}"></h3>
<hr/>
<div shiro:hasPermission="user:add">
进入用户添加功能: <a href="add">用户添加</a><br/>
</div>
<div shiro:hasPermission="user:update">
进入用户更新功能: <a href="update">用户更新</a><br/>
</div>
<a href="toLogin">登录</a>
</body>
</html>