一、shiro 内部结构
1、shiro 包含的组件
shiro主要包括认证器(Authenticator),授权器(Authrizer),session会话管理(SessionManager),加密处理(Cryptography),记住我(remember me),对权限的缓存(CacheManager)
2、shiro 各组件介绍
Subject:主体,可以理解为 与应用交互的用户 subject 里包含用户的所有信息 如 用户信息,用户角色,用户权限,是否登录等等;
SecurityManager:安全管理器 shiro通过securityManager 管理着整个shiro各个模块。
Authenticator:认证器,负责认证用户是否是合法用户 Authenticator具体认证过程 是通过realm 来处理用户是否是合法用户;
Authrizer:授权器, 负责给用户授权 Authrizer 具体授权是通过realm 来获取用户所具有的权限;
Realm:可以有1个或多个Realm,也可以自定义realm realm在shiro框架中很重要 负责处理用户的认证信息和授权信息;
SessionManager:负责session的管理;
SessionDAO:对session的curd操作;
CacheManager:可以对用户权限信息进行缓存 提供性能;
Cryptography:密码模块, 对密码进行加密加盐处理;
shiro 通过securityManager认证管理器来管理认证器,授权器,会话管理器,缓存,sessionDao,realm ,应用端(app)通过subject 和securityManager认证管理器交互,认证管理器负责代理到认证器或者授权器,认证器或者授权器最后通过realm获取用户的认证信息或者授权信息
二、shiro 依赖的pom文件
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
三、通过ini文件方式 使用shiro
1、 用户认证Demo
shiro1.ini 文件内容如下
#users 模拟真实用户
[users]
# 格式 用户名=密码
xiaoming=123
xiaohong=123456
@Test
public void testAuthenticate(){
//1、加载ini文件 通过ini文件创建IniSecurityManagerFactory
IniSecurityManagerFactory managerFactory = new IniSecurityManagerFactory("classpath:shiro1.ini");
//2、获取安全管理器(SecurityManager)
SecurityManager instance = managerFactory.getInstance();
//3、把当前SecurityManager 绑定到当前环境
SecurityUtils.setSecurityManager(instance);
//4、获取subject 主体对象
Subject subject = SecurityUtils.getSubject();
//5、设置用户名和密码
UsernamePasswordToken token = new UsernamePasswordToken("xiaoming","123");
//6、登录
subject.login(token);
//7、查看用户是否登录成功 true成功 false 失败
System.out.println(subject.isAuthenticated());
}
2、用户授权Demo
shiro2.ini 文件内容如下
#users 模拟真实用户
[users]
# 格式 用户名=密码,对应的角色 如 xiaoming 密码 是123 是admin角色 对应就拥有 user:save,user:delete,user:update,user:find权限
xiaoming=123,admin
xiaohong=123456,manager
#roles 模拟 角色信息
[roles]
#格式 角色名=角色对应拥有的权限 如admin角色拥有user:save,user:delete,user:update,user:find 权限 manager角色拥有 user:find权限
admin=user:save,user:delete,user:update,user:find
manager=user:find
@Test
public void testAuthrizer(){
//1、加载ini文件 通过ini文件创建IniSecurityManagerFactory
IniSecurityManagerFactory managerFactory = new IniSecurityManagerFactory("classpath:shiro2.ini");
//2、获取安全管理器(SecurityManager)
SecurityManager instance = managerFactory.getInstance();
//3、把当前SecurityManager 绑定到当前环境
SecurityUtils.setSecurityManager(instance);
//4、获取subject 主体对象
Subject subject = SecurityUtils.getSubject();
//5、设置用户名和密码
UsernamePasswordToken token = new UsernamePasswordToken("xiaohong","123456");
//6、登录
subject.login(token);
//7、查看用户拥有的角色和权限
System.out.println(subject.hasRole("manager"));
System.out.println(subject.isPermitted("user:update"));
}
3、自定义realm Demo
shiro3.ini文件内容如下
[main]
#声明自定义的realm名字 = 自定义realm类全限定名
customerRealm=com.xiao.shiro.CustomerRealm
#注册realm到securityManager中
securityManager.realms=$customerRealm
自定义realm
public class CustomerRealm extends AuthorizingRealm {
//自定义realm的名称 因为shiro框架中可能存在多个realm 根据realm的名称 来判断用哪个realm做处理
@Override
public void setName(String name) {
super.setName("CustomerRealm");
}
//授权 当主体(subject)调用获取用户角色时 会调用doGetAuthorizationInfo这个方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("授权开始");
//可以从principalCollection 获取用户信息
String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRoles(Arrays.asList("manager","admin"));
info.addStringPermission("user:update");
return info;
}
//认证 当主体调用(subject)调用用户登录时 会调用 doGetAuthenticationInfo 这个方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("认证开始");
//用户登录的时候 subject.login(token); 传进来的token类型 是 UsernamePasswordToken
//所以可以把authenticationToken 强转成UsernamePasswordToken
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
String pwd = new String (token.getPassword());
//模拟从数据库中查出来的用户名密码 分别是 xiaoming 123
if ("xiaoming".equals(username) && "123".equals(pwd)) {
//认证通过 把用户信息存到AuthenticationInfo 对象里
//SimpleAuthenticationInfo 三个参数 如下
//1、Object principal 用户信息 可以是任何类型的对象
//2、Object credentials 密码
//3、String realmName 当前realm的名称
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,pwd,getName());
return authenticationInfo;
}
//认证不通过 抛出异常
throw new RuntimeException("登录失败");
}
}
@Test
public void testCustomerRealm(){
//1、加载ini文件 通过ini文件创建IniSecurityManagerFactory
IniSecurityManagerFactory managerFactory = new IniSecurityManagerFactory("classpath:shiro3.ini");
//2、获取安全管理器(SecurityManager)
SecurityManager instance = managerFactory.getInstance();
//3、把当前SecurityManager 绑定到当前环境
SecurityUtils.setSecurityManager(instance);
//4、获取subject 主体对象
Subject subject = SecurityUtils.getSubject();
//5、设置用户名和密码
UsernamePasswordToken token = new UsernamePasswordToken("xiaoming","123");
//6、登录 subject登录时会调用realm里的doGetAuthenticationInfo方法
subject.login(token);
//7、查看用户拥有的角色和权限 当subject获取角色或者权限时 会调用realm里的doGetAuthorizationInfo方法
System.out.println(subject.hasRole("manager"));
System.out.println(subject.isPermitted("user:find"));
}
4、使用缓存 存用户角色信息和权限信息 Demo
使用ehcache作为缓存
shiro-ehcache.xml 配置信息
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
@Test
public void testCacheManager(){
//1、加载ini文件 通过ini文件创建IniSecurityManagerFactory
IniSecurityManagerFactory managerFactory = new IniSecurityManagerFactory("classpath:shiro3.ini");
//2、获取安全管理器(SecurityManager) 如果启用CacheManager 需要CachingSecurityManager 对象
CachingSecurityManager instance = (CachingSecurityManager) managerFactory.getInstance();
//3、创建缓存管理对象 (这里使用ehcache做缓存所以使用EhCacheManager) 可以使用其他的缓存对象
EhCacheManager ehCacheManager = new EhCacheManager();
//4、读取ehcache配置文件
ehCacheManager.setCacheManagerConfigFile("classpath:shiro-ehcache.xml");
//5、把EhCacheManager对象 设置到CachingSecurityManager 安全管理器中
instance.setCacheManager(ehCacheManager);
//6、把当前SecurityManager 绑定到当前环境
SecurityUtils.setSecurityManager(instance);
//7、获取subject 主体对象
Subject subject = SecurityUtils.getSubject();
//8、设置用户名和密码
UsernamePasswordToken token = new UsernamePasswordToken("xiaoming","123");
//9、登录
subject.login(token);
//10、查看用户拥有的角色和权限 第一次获取权限信息的时候会调用realm中的doGetAuthorizationInfo 方法 第二次 直接从缓存中获取 不走realm
System.out.println(subject.hasRole("manager"));
System.out.println(subject.isPermitted("user:find"));
}
5、清除缓存的用户信息
@Test
public void testCacheManager(){
//1、加载ini文件 通过ini文件创建IniSecurityManagerFactory
IniSecurityManagerFactory managerFactory = new IniSecurityManagerFactory("classpath:shiro3.ini");
//2、获取安全管理器(SecurityManager) 如果启用CacheManager 需要CachingSecurityManager 对象
CachingSecurityManager instance = (CachingSecurityManager) managerFactory.getInstance();
//3、创建缓存管理对象 (这里使用ehcache做缓存所以使用EhCacheManager) 可以使用其他的缓存对象
EhCacheManager ehCacheManager = new EhCacheManager();
//4、读取ehcache配置文件
ehCacheManager.setCacheManagerConfigFile("classpath:shiro-ehcache.xml");
//5、把EhCacheManager对象 设置到CachingSecurityManager 安全管理器中
instance.setCacheManager(ehCacheManager);
//6、把当前SecurityManager 绑定到当前环境
SecurityUtils.setSecurityManager(instance);
//7、获取subject 主体对象
Subject subject = SecurityUtils.getSubject();
//8、设置用户名和密码
UsernamePasswordToken token = new UsernamePasswordToken("xiaoming","123");
//9、登录
subject.login(token);
//10、查看用户拥有的角色和权限 第一次获取权限信息的时候会调用realm中的doGetAuthorizationInfo 方法 第二次 直接从缓存中获取 不走realm
System.out.println(subject.hasRole("manager"));
System.out.println(subject.isPermitted("user:find"));
//获取ehcache 用户缓存信息
Cache<Object, Object> cache = ehCacheManager.getCache("CustomerRealm.authorizationCache");
//清除 xiaoming用户对应的缓存信息
cache.remove(new SimplePrincipalCollection("xiaoming", "CustomerRealm"));
//清除缓存后 获取用户角色和权限信息 重新从realm中获取
System.out.println(subject.hasRole("manager"));
System.out.println(subject.isPermitted("user:find"));
}
三、使用springboot 集成 shiro
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xiao.shiro</groupId>
<artifactId>shirodemo</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.0.2</version>
</dependency>
<!--shiro核心-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
<!-- ehcache -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.11</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.3.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.44</version>
</dependency>
<!--spring和shiro整合 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!--shiro与redis整合-->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.0.0</version>
</dependency>
<!--thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
</project>
2、自定义realm
public class CustomerRealm extends AuthorizingRealm {
@Autowired
private UserInfoService userInfoService;
//自定义realm的名称 因为shiro框架中可能存在多个realm 根据realm的名称 来判断用哪个realm做处理
@Override
public void setName(String name) {
super.setName("CustomerRealm");
}
//授权 当主体(subject)调用获取用户角色时 会调用doGetAuthorizationInfo这个方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("授权开始");
//可以从principalCollection 获取用户信息
UserInfo userInfo = (UserInfo) principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
UserDto userExtInfo = userInfoService.findUserExtInfo(userInfo.getId());
info.addRoles(userExtInfo.getRoles());
info.addStringPermissions(userExtInfo.getPermissions());
return info;
}
//认证 当主体调用(subject)调用用户登录时 会调用 doGetAuthenticationInfo 这个方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("认证开始");
//用户登录的时候 subject.login(token); 传进来的token类型 是 UsernamePasswordToken
//所以可以把authenticationToken 强转成UsernamePasswordToken
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
String pwd = new String (token.getPassword());
UserInfo user = userInfoService.findUserByUserNameAndPwd(username, pwd);
//模拟从数据库中查出来的用户名密码 分别是 xiaoming 123
if (user != null) {
//认证通过 把用户信息存到AuthenticationInfo 对象里
//SimpleAuthenticationInfo 三个参数 如下
//1、Object principal 用户信息 可以是任何类型的对象
//2、Object credentials 密码
//3、String realmName 当前realm的名称
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, pwd, getName());
return authenticationInfo;
}
//认证不通过 抛出异常
throw new RuntimeException("登录失败");
}
}
3、shiro 配置类
@Configuration
public class ShiroConfig {
//把自定义realm 交给spring容器管理
@Bean
public CustomerRealm getCustomerRealm(){
return new CustomerRealm();
}
@Bean
public SecurityManager getSecurityManager(CustomerRealm realm){
//创建安全管理器 并把realm交给管理器管理
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(realm);
return securityManager;
}
//配置 shiro过滤条件和跳转页面
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(securityManager);
//登录页面
bean.setLoginUrl("/tologin");
//授权失败跳转页面
bean.setUnauthorizedUrl("/toUnauthorized");
//必须用LinkedHashMap 要保证过滤的顺序
Map<String, String> map = new LinkedHashMap<>();
//anon 匿名访问 也就是说不用登录和授权就能访问
//authc 需要认证登录才能访问
//logout 注销 退出登录 跳转到 bean.setLoginUrl()方法设置的页面
//perms[xx] 有xx的权限才能访问
//roles[xx] 有xx的角色才能访问
map.put("/user/index", "anon");
//表示有select-user的权限才可以访问
map.put("/user/select", "perms[select-user]");
//表示系统管理员的角色才能访问
map.put("/user/delete", "roles[系统管理员]");
//只有认证登录才能访问
map.put("/user/**", "authc");
//设置请求的过滤链 过滤链是有顺序的 需要认证的一般放在匿名访问的后面
bean.setFilterChainDefinitionMap(map);
return bean;
}
}
4、测试controller
@Controller
public class TestController {
@RequestMapping("/login")
public String login(UserInfo userInfo) {
try {
UsernamePasswordToken upt = new UsernamePasswordToken(userInfo.getUsername(), userInfo.getPassword());
SecurityUtils.getSubject().login(upt);
return "登录成功";
} catch (Exception e) {
e.printStackTrace();
return "登录失败";
}
}
@RequestMapping("/user/index")
@ResponseBody
public String index(){
return "访问index成功";
}
@RequestMapping("/user/select")
@ResponseBody
public String select(){
return "访问select成功";
}
@RequestMapping("/user/delete")
@ResponseBody
public String delete(){
return "访问delete成功";
}
@RequestMapping("/user/login")
@ResponseBody
public String login(){
return "访问login成功";
}
//跳登录页
@RequestMapping("/tologin")
public String tologin(){
return "login";
}
//跳未授权页面
@RequestMapping("/toUnauthorized")
public String toUnauthorized(){
return "unauthorized";
}
}
5、shiro注解的方式 做权限控制
(1)、pom文件添加依赖 参考上面依赖即可
(2)、shiro 配置类 增加 DefaultAdvisorAutoProxyCreator,AuthorizationAttributeSourceAdvisor 这两个对象的配置
@Configuration
public class ShiroConfig {
//开启shiro注解的方式 要配置 ,DefaultAdvisorAutoProxyCreator,AuthorizationAttributeSourceAdvisor 这两个对象
@Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
//开启对springaop的代理
autoProxyCreator.setProxyTargetClass(true);
return autoProxyCreator;
}
@Bean
public CustomerRealm getCustomerRealm(){
return new CustomerRealm();
}
@Bean
public SecurityManager getSecurityManager(CustomerRealm realm){
//创建安全管理器 并把realm交给管理器管理
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(realm);
return securityManager;
}
//配置 启动shiro注解
@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
//配置 shiro过滤条件和跳转页面
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(securityManager);
//登录页面
bean.setLoginUrl("/tologin");
//授权失败跳转页面
bean.setUnauthorizedUrl("/toUnauthorized");
//必须用LinkedHashMap 要保证过滤的顺序
Map<String, String> map = new LinkedHashMap<>();
//anon 匿名访问 也就是说不用登录和授权就能访问
//authc 需要认证登录才能访问
//logout 注销 退出登录 跳转到 bean.setLoginUrl()方法设置的页面
//perms[xx] 有xx的权限才能访问
//roles[xx] 有xx的角色才能访问
map.put("/user/index", "anon");
map.put("/login", "anon");
//表示有select-user的权限才可以访问
map.put("/user/select", "perms[select-user]");
//表示系统管理员的角色才能访问
map.put("/user/delete", "roles[系统管理员]");
//只有认证登录才能访问
map.put("/**", "authc");
//设置请求的过滤链 过滤链是有顺序的 需要认证的一般放在匿名访问的后面
bean.setFilterChainDefinitionMap(map);
return bean;
}
(3)、测试controller
@Controller
public class TestController {
//跳登录页
@RequestMapping("/tologin")
public String tologin(){
return "login";
}
//跳未授权页面
@RequestMapping("/toUnauthorized")
public String toUnauthorized(){
return "unauthorized";
}
//同时有user-add和user-delete 权限才能访问
@RequiresPermissions(value = {"user-add","user-delete"})
@RequestMapping("/testPermission")
@ResponseBody
public String testPermission(){
return "测试 Permission 成功";
}
//有管理员角色 才能访问
@RequiresRoles(value = "管理员")
@RequestMapping("/testRoles")
@ResponseBody
public String tesRoles(){
return "测试 role 成功";
}
}
shiro基于过滤器链(如 map.put("/**", "authc"))的方式和基于注解的方式(如@RequiresRoles) 再没有权限访问的时候处理方式不同
过滤链的方式 如果没有权限会跳转到 setUnauthorizedUrl("/toUnauthorized") 设置的地址
注解的方式 如果没有权限会抛AuthorizationException异常 可以自定义一个全局异常 来处理注解方式没有权限访问 代码如下
@ControllerAdvice
public class CustomerExceptionHandler {
//捕获AuthorizationException类型的异常
@ExceptionHandler(value = AuthorizationException.class)
@ResponseBody
public String error(HttpServletRequest request, HttpServletResponse response,AuthorizationException e) {
return "未授权";
}
}
四、shiro 密码加密
public static void main(String[] args) {
//三个参数如下
//1、Object source 原始密码
//2、Object salt 盐值
//3、int hashIterations 加盐几次
Md5Hash pwd = new Md5Hash("12345", "salt", 2);
System.out.println(pwd.toString());
}
五、springboot 整合shiro的 会话session管理 把会话放到redis中存储
shiro 默认提供了三个sessionManager 默认实现
(1). DefaultSessionManager:用于JavaSE环境
(2). ServletContainerSessionManager:用于Web环境,web环境默认使用这个实现类 session信息存在httpSession里
(3). DefaultWebSessionManager:用于web环境,自己维护会话 可以把session信息存在指定的地方 如mysql,redis
1、引入shiro-redis 依赖
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.0.0</version>
</dependency>
2、再application.yml引入redis 配置
spring:
redis:
host: 127.0.0.1
port: 6379
3、自定义shiro会话管理器
public class CustomerSessionManager extends DefaultWebSessionManager {
/**
* 头信息中具有sessionid
* 请求头:Authorization: sessionid
*
* 指定sessionId的获取方式
*/
@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
//获取请求头Authorization中的数据
String id = WebUtils.toHttp(request).getHeader("Authorization");
if(StringUtils.isEmpty(id)) {
//如果没有携带,生成新的sessionId
return super.getSessionId(request,response);
}else{
//指定sessionId的来源 指定从请求头里获取sessionId 不指定默认从cookie中获取
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header");
//sessionId
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return id;
}
}
}
4、配置shiro 会话管理 放到redis中存储
package com.xiao.shiro.config;
import com.xiao.shiro.realm.CustomerRealm;
import com.xiao.shiro.session.CustomerSessionManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
public CustomerRealm getCustomerRealm(){
return new CustomerRealm();
}
@Bean
public SecurityManager getSecurityManager(CustomerRealm realm){
//创建安全管理器 并把realm交给管理器管理
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(realm);
//将自定义的会话管理器注册到安全管理器中
securityManager.setSessionManager(sessionManager());
//将自定义的redis缓存管理器注册到安全管理器中
securityManager.setCacheManager(cacheManager());
return securityManager;
}
//配置 启动shiro注解
@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
//配置 shiro过滤条件和跳转页面
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(securityManager);
//登录页面
bean.setLoginUrl("/tologin");
//授权失败跳转页面
bean.setUnauthorizedUrl("/toUnauthorized");
//必须用LinkedHashMap 要保证过滤的顺序
Map<String, String> map = new LinkedHashMap<>();
//anon 匿名访问 也就是说不用登录和授权就能访问
//authc 需要认证登录才能访问
//logout 注销 退出登录 跳转到 bean.setLoginUrl()方法设置的页面
//perms[xx] 有xx的权限才能访问
//roles[xx] 有xx的角色才能访问
map.put("/user/index", "anon");
map.put("/login", "anon");
//表示有select-user的权限才可以访问
map.put("/user/select", "perms[select-user]");
//表示系统管理员的角色才能访问
map.put("/user/delete", "roles[系统管理员]");
//只有认证登录才能访问
map.put("/**", "authc");
//设置请求的过滤链 过滤链是有顺序的 需要认证的一般放在匿名访问的后面
bean.setFilterChainDefinitionMap(map);
return bean;
}
//开启shiro注解的方式 要配置 ,DefaultAdvisorAutoProxyCreator,AuthorizationAttributeSourceAdvisor 这两个对象
@Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
//开启对springaop的代理
autoProxyCreator.setProxyTargetClass(true);
return autoProxyCreator;
}
@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() {
CustomerSessionManager sessionManager = new CustomerSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
return sessionManager;
}
/**
* 4.缓存管理器
*/
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
}
5、AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, pwd, getName());
这里的user对象要实现 Serializable ,AuthCachePrincipal两个接口
public class UserInfo implements Serializable,AuthCachePrincipal {
private Long id;
private String username;
private String password;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
//从写getAuthCacheKey方法即可
@Override
public String getAuthCacheKey() {
return null;
}
六、完整代码
https://github.com/zhougn18/shiro.git
来源:oschina
链接:https://my.oschina.net/xiaozhou18/blog/4279677