Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
主要功能
三个核心组件:Subject、SecurityManager 和 Realms.
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。
1、简单了解Shiro的API
添加相关依赖pom.xml
org.springframework.boot
spring-boot-starter
org.apache.shiro
shiro-core
1.2.3
org.projectlombok
lombok
org.slf4j
slf4j-api
compile
org.slf4j
log4j-over-slf4j
org.springframework.boot
spring-boot-starter-test
test
编写shiro配置文件,在resources目录下编写shiro配置文件,shiro.ini
#用户名=密码,角色1,角色2...,角色n
[users]
root = secret, admin
guest = guest, guest
test = 123456, role1, role2
# -----------------------------------------------------------------------------
# Roles with assigned permissions
# roleName = perm1, perm2, ..., permN
# 角色名=权限1,权限2...权限n
# -----------------------------------------------------------------------------
[roles]
admin = *
guest = guest
role1=perm1,perm2
role2=perm3
测试类
package com.example.demo.shiro;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
/**
* 路径:com.example.demo.shiro
* 类名:
* 功能:《用一句描述一下》
* 备注:
* 创建人:typ
* 创建时间:2018/10/8 17:50
* 修改人:
* 修改备注:
* 修改时间:
*/
@Slf4j
public class ShiroTest {
public static void main(String[] args) {
//创建 SecurityManager
Factory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//解析配置文件,并返回一些 SecurityManager
SecurityManager securityManager = factory.getInstance();
//将SecurityManager
SecurityUtils.setSecurityManager(securityManager);
//安全操作,Subject
Subject currentUser = SecurityUtils.getSubject();
//测试在应用的当前会话中设置的属性
Session session = currentUser.getSession();
//放进去一个key
session.setAttribute("someKey", "aValue");
//根据key获取value
String value = (String) session.getAttribute("someKey");
//比较拿到的值和原来的值是否一致
if ("aValue".equals(value)) {
log.info("检索到正确的值[" + value + "]");
}
//尝试进行登录用户,如果登录失败了,我们进行一些处理
if (!currentUser.isAuthenticated()) {
//如果用户没有登录过
UsernamePasswordToken token = new UsernamePasswordToken("test", "123456");
//是否记住用户
token.setRememberMe(true);
try {
currentUser.login(token);
//当我们获登录用户之后
log.info("用户 [" + currentUser.getPrincipal() + "] 登陆成功");
// 查看用户是否有指定的角色
if (currentUser.hasRole("admin")) {
log.info("您有admin角色");
} else {
log.info("您没有admin角色");
}
if (currentUser.hasRole("role1")) {
log.info("您有role1角色");
} else {
log.info("您没有role1角色");
}
// 查看用户是否有某个权限
if (currentUser.isPermitted("perm1")) {
log.info("您有perm1权限");
} else {
log.info("您没有perm1权限");
}
if (currentUser.isPermitted("guest")) {
log.info("您有guest权限");
} else {
log.info("您没有guest权限");
}
//退出
currentUser.logout();
} catch (UnknownAccountException uae) {
log.info(token.getPrincipal() + "账户不存在");
} catch (IncorrectCredentialsException ice) {
log.info(token.getPrincipal() + "密码不正确");
} catch (LockedAccountException lae) {
log.info(token.getPrincipal() + "用户被锁定了 ");
} catch (AuthenticationException ae) {
//无法判断是什么错了
log.info(ae.getMessage());
}
}
}
}
运行程序,查看结果如下:
21:41:25.606 [main] DEBUG org.apache.shiro.subject.support.DefaultSubjectContext - No SecurityManager available in subject context map. Falling back to SecurityUtils.getSecurityManager() lookup.
21:41:25.610 [main] INFO com.example.demo.shiro.ShiroTest - 用户 [test] 登陆成功
21:41:25.610 [main] INFO com.example.demo.shiro.ShiroTest - 您没有admin角色
21:41:25.610 [main] INFO com.example.demo.shiro.ShiroTest - 您有role1角色
21:41:25.610 [main] INFO com.example.demo.shiro.ShiroTest - 您有perm1权限
21:41:25.634 [main] INFO com.example.demo.shiro.ShiroTest - 您没有guest权限
21:41:25.634 [main] DEBUG org.apache.shiro.mgt.DefaultSecurityManager - Logging out subject with primary principal test
2、Shiro+MySQL动态权限验证
数据库设计:
用户表(SHIRO_USER)、用户角色表(SHIRO_USER_ROLE)、角色权限表(SHIRO_ROLE_PERMISSION)
SQL如下:
# Host: 127.0.0.1 (Version 5.7.21)
# Date: 2018-10-08 22:26:32
# Generator: MySQL-Front 6.0 (Build 2.20)
#
# Structure for table "shiro_user"
#
DROP TABLE IF EXISTS `shiro_user`;
CREATE TABLE `shiro_user` (
`id` varchar(32) DEFAULT NULL,
`user_name` varchar(50) DEFAULT NULL,
`password` varchar(32) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
#
# Data for table "shiro_user"
#
INSERT INTO `shiro_user` VALUES ('1','test@shiro.com','123456');
#
# Structure for table "shiro_user_role"
#
DROP TABLE IF EXISTS `shiro_user_role`;
CREATE TABLE `shiro_user_role` (
`id` varchar(32) DEFAULT NULL,
`role_name` varchar(50) DEFAULT NULL,
`user_name` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
#
# Data for table "shiro_user_role"
#
INSERT INTO `shiro_user_role` VALUES ('1','admin','test@shiro.com'),('2','test','test@shiro.com');
#
# Structure for table "shiro_role_permission"
#
DROP TABLE IF EXISTS `shiro_role_permission`;
CREATE TABLE `shiro_role_permission` (
`id` varchar(32) DEFAULT NULL,
`role_name` varchar(50) DEFAULT NULL,
`perm_name` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
#
# Data for table "shiro_role_permission"
#
INSERT INTO `shiro_role_permission` VALUES ('1','admin','perm1'),('2','test','guest');
添加数据库相关的依赖,pom.xml
org.springframework.boot
spring-boot-starter
org.apache.shiro
shiro-core
1.2.3
org.projectlombok
lombok
org.slf4j
slf4j-api
compile
org.slf4j
log4j-over-slf4j
mysql
mysql-connector-java
5.1.46
com.alibaba
druid
org.springframework.boot
spring-boot-starter-test
test
编写shiro配置文件,在resources目录下编写shiro配置文件,shiro-mysql.ini
[main]
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://127.0.0.1:3306/mc_config
dataSource.username=root
dataSource.password=admin
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
#是否检查权限
jdbcRealm.permissionsLookupEnabled = true
jdbcRealm.dataSource=$dataSource
#重写sql语句
#根据用户名查询出密码
jdbcRealm.authenticationQuery = select PASSWORD from SHIRO_USER where USER_NAME = ?
#根据用户名查询出角色
jdbcRealm.userRolesQuery = select ROLE_NAME from SHIRO_USER_ROLE where USER_NAME = ?
#根据角色名查询出权限
jdbcRealm.permissionsQuery = select PERM_NAME from SHIRO_ROLE_PERMISSION WHERE ROLE_NAME = ?
securityManager.realms=$jdbcRealm
测试类
package com.example.demo.shiro;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.apache.shiro.mgt.SecurityManager;
/**
* 路径:com.example.demo.shiro
* 类名:
* 功能:Shiro+MySQL动态权限验证
* 备注:
* 创建人:typ
* 创建时间:2018/10/8 21:57
* 修改人:
* 修改备注:
* 修改时间:
*/
@Slf4j
public class ShiroMysqlTest {
public static void main(String[] args) {
Factory factory = new IniSecurityManagerFactory("classpath:shiro-mysql.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject currentUser = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("test@shiro.com", "123456");
//是否记住用户
token.setRememberMe(true);
try {
currentUser.login(token);
//当我们获登录用户之后
log.info("用户 [" + currentUser.getPrincipal() + "] 登陆成功");
//查看用户是否有角色
if (currentUser.hasRole("admin")) {
log.info("您有admin角色");
} else {
log.info("您没有admin角色");
}
if (currentUser.hasRole("test")) {
log.info("您有test角色");
} else {
log.info("您没有test角色");
}
// 查看用户是否有某个权限
if (currentUser.isPermitted("perm1")) {
log.info("您有perm1权限");
} else {
log.info("您没有perm1权限");
}
if (currentUser.isPermitted("guest")) {
log.info("您有guest权限");
} else {
log.info("您没有guest权限");
}
//退出
currentUser.logout();
} catch (UnknownAccountException uae) {
log.info(token.getPrincipal() + "账户不存在");
} catch (IncorrectCredentialsException ice) {
log.info(token.getPrincipal() + "密码不正确");
} catch (LockedAccountException lae) {
log.info(token.getPrincipal() + "用户被锁定了 ");
} catch (AuthenticationException ae) {
//无法判断是什么错了
log.info(ae.getMessage());
}
}
}
运行程序,查看结果如下:
21:48:00.094 [main] INFO com.example.demo.shiro.ShiroMysqlTest - 用户 [test@shiro.com] 登陆成功
21:48:00.098 [main] DEBUG org.apache.shiro.realm.AuthorizingRealm - No authorizationCache instance set. Checking for a cacheManager...
21:48:00.098 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set. Authorization cache cannot be obtained.
21:48:00.102 [main] INFO com.example.demo.shiro.ShiroMysqlTest - 您有admin角色
21:48:00.102 [main] DEBUG org.apache.shiro.realm.AuthorizingRealm - No authorizationCache instance set. Checking for a cacheManager...
21:48:00.102 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set. Authorization cache cannot be obtained.
21:48:00.110 [main] INFO com.example.demo.shiro.ShiroMysqlTest - 您有test角色
21:48:00.110 [main] DEBUG org.apache.shiro.realm.AuthorizingRealm - No authorizationCache instance set. Checking for a cacheManager...
21:48:00.110 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set. Authorization cache cannot be obtained.
21:48:00.114 [main] INFO com.example.demo.shiro.ShiroMysqlTest - 您有perm1权限
21:48:00.114 [main] DEBUG org.apache.shiro.realm.AuthorizingRealm - No authorizationCache instance set. Checking for a cacheManager...
21:48:00.114 [main] INFO org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set. Authorization cache cannot be obtained.
21:48:00.122 [main] INFO com.example.demo.shiro.ShiroMysqlTest - 您有guest权限
21:48:00.122 [main] DEBUG org.apache.shiro.mgt.DefaultSecurityManager - Logging out subject with primary principal test@shiro.com
3. SpringBoot整合mybatis、shiro、redis实现基于数据库动态权限管理系统实例
数据库设计:
使用maven管理jar包,添加相关依赖:
4.0.0
com.example.demo.shiro
shiro-mybatis-redis
0.0.1-SNAPSHOT
jar
shiro-mybatis-redis
org.springframework.boot
spring-boot-starter-parent
1.5.9.RELEASE
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
tk.mybatis
mapper-spring-boot-starter
1.1.3
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.0
tk.mybatis
mapper
3.4.2
com.alibaba
druid
1.0.28
mysql
mysql-connector-java
5.1.46
org.springframework.boot
spring-boot-starter-thymeleaf
com.github.theborakompanioni
thymeleaf-extras-shiro
1.2.1
org.apache.shiro
shiro-spring
1.3.2
net.sourceforge.nekohtml
nekohtml
1.9.22
org.crazycake
shiro-redis
2.4.2.1-RELEASE
shiro-core
org.apache.shiro
com.github.pagehelper
pagehelper-spring-boot-starter
1.1.2
org.projectlombok
lombok
provided
org.springframework.boot
spring-boot-maven-plugin
application.yml配置文件:
server:
port: 8081
#数据源配置
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://127.0.0.1:3306/shiro
username: root
password: admin
initial-size: 1
min-idle: 1
max-active: 20
thymeleaf:
cache: false
mode: LEGACYHTML5
redis:
host: 192.168.0.131
port: 6379
password: root
timeout: 5000
commandTimeout: 5000
mybatis:
type-aliases-package: com.example.demo.shiro.entity
mapper-locations: classpath:mapper/*.xml
configLocation: classpath:mybatis.xml
mapper:
identity: MYSQL
mappers: com.example.demo.shiro.utils.MyMapper
not-empty: false
#分页配置
pagehelper:
helper-dialect: mysql
offset-as-page-num: true
reasonable: true
row-bounds-with-count: true
support-methods-arguments: true
params: count=countSql
logging:
level: debug
shiro的配置文件:
package com.example.demo.shiro.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.example.demo.shiro.entity.Resources;
import com.example.demo.shiro.service.ResourcesService;
import com.example.demo.shiro.shiro.MyShiroRealm;
import com.github.pagehelper.util.StringUtil;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
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.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.shiro.mgt.SecurityManager;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* 路径:com.example.demo.shiro.config
* 类名:
* 功能:《用一句描述一下》
* 备注:
* 创建人:typ
* 创建时间:2018/10/9 10:22
* 修改人:
* 修改备注:
* 修改时间:
*/
@Configuration
public class ShiroConfig {
@Autowired
private ResourcesService resourcesService;
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.password}")
private String password;
@Bean
public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 方法名:
* 功能:ShiroDialect,为了在thymeleaf里使用shiro的标签的bean
* 描述:
* 创建人:typ
* 创建时间:2018/10/9 10:24
* 修改人:
* 修改描述:
* 修改时间:
*/
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
/**
* 方法名:
* 功能:ShiroFilterFactoryBean 处理拦截资源文件问题。
* 注意:单独一个ShiroFilterFactoryBean配置是或报错的,因为在初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
* 描述: Filter Chain定义说明
* 1、一个URL可以配置多个Filter,使用逗号分隔
* 2、当设置多个过滤器时,全部验证通过,才视为通过
* 3、部分过滤器可指定参数,如perms,roles
* 创建人:typ
* 创建时间:2018/10/9 10:25
* 修改人:
* 修改描述:
* 修改时间:
*/
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){
System.out.println("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/usersPage");
//未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
//拦截器.
Map filterChainDefinitionMap = new LinkedHashMap();
//配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/css/**","anon");
filterChainDefinitionMap.put("/js/**","anon");
filterChainDefinitionMap.put("/img/**","anon");
filterChainDefinitionMap.put("/font-awesome/**","anon");
//:这是一个坑呢,一不小心代码就不好使了;
//
//自定义加载权限资源关系
List resourcesList = resourcesService.queryAll();
for(Resources resources:resourcesList){
if (StringUtil.isNotEmpty(resources.getResurl())) {
String permission = "perms[" + resources.getResurl()+ "]";
filterChainDefinitionMap.put(resources.getResurl(),permission);
}
}
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置realm.
securityManager.setRealm(myShiroRealm());
// 自定义缓存实现 使用redis
securityManager.setCacheManager(cacheManager());
// 自定义session管理 使用redis
securityManager.setSessionManager(sessionManager());
return securityManager;
}
@Bean
public MyShiroRealm myShiroRealm(){
MyShiroRealm myShiroRealm = new MyShiroRealm();
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm;
}
/**
* 方法名:
* 功能:凭证匹配器
* (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了所以我们需要修改下doGetAuthenticationInfo中的代码;)
* 描述:
* 创建人:typ
* 创建时间:2018/10/9 10:29
* 修改人:
* 修改描述:
* 修改时间:
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashAlgorithmName("md5");
//散列的次数,比如散列两次,相当于 md5(md5(""));
hashedCredentialsMatcher.setHashIterations(2);
return hashedCredentialsMatcher;
}
/**
* 方法名:
* 功能:开启shiro aop注解支持.
* 描述:使用代理方式;所以需要开启代码支持;
* 创建人:typ
* 创建时间:2018/10/9 10:30
* 修改人:
* 修改描述:
* 修改时间:
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* 方法名:
* 功能:配置shiro redisManager
* 描述:使用的是shiro-redis开源插件
* 创建人:typ
* 创建时间:2018/10/9 10:30
* 修改人:
* 修改描述:
* 修改时间:
*/
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost(host);
redisManager.setPort(port);
// 配置缓存过期时间
redisManager.setExpire(1800);
redisManager.setTimeout(timeout);
redisManager.setPassword(password);
return redisManager;
}
/**
* 方法名:
* 功能:cacheManager 缓存 redis实现
* 描述:使用的是shiro-redis开源插件
* 创建人:typ
* 创建时间:2018/10/9 10:31
* 修改人:
* 修改描述:
* 修改时间:
*/
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
/**
* 方法名:
* 功能:RedisSessionDAO shiro sessionDao层的实现 通过redis
* 描述:使用的是shiro-redis开源插件
* 创建人:typ
* 创建时间:2018/10/9 10:31
* 修改人:
* 修改描述:
* 修改时间:
*/
@Bean
public RedisSessionDAO redisSessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}
/**
* 方法名:
* 功能: shiro session的管理
* 描述:
* 创建人:typ
* 创建时间:2018/10/9 10:31
* 修改人:
* 修改描述:
* 修改时间:
*/
@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
return sessionManager;
}
}
redis的配置:
package com.example.demo.shiro.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
/**
* 路径:com.example.demo.shiro.config
* 类名:
* 功能:《用一句描述一下》
* 备注:
* 创建人:typ
* 创建时间:2018/10/9 10:09
* 修改人:
* 修改备注:
* 修改时间:
*/
@Slf4j
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private Integer port;
@Value("${spring.redis.timeout}")
private Integer timeout;
@Value("${spring.redis.password}")
private String password;
@Bean
public JedisPool redisPoolFactory(){
log.info("JedisPool注入成功!!");
log.info("redis地址:" + host + ":" + port);
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
JedisPool jedisPool = new JedisPool(jedisPoolConfig,host, port,timeout,password);
return jedisPool;
}
}
配置自定义的Realm
package com.example.demo.shiro.shiro;
import com.example.demo.shiro.entity.Resources;
import com.example.demo.shiro.entity.User;
import com.example.demo.shiro.service.ResourcesService;
import com.example.demo.shiro.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.mgt.RealmSecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.util.ByteSource;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.Resource;
import java.util.*;
/**
* 路径:com.example.demo.shiro.shiro
* 类名:
* 功能:《用一句描述一下》
* 备注:
* 创建人:typ
* 创建时间:2018/10/9 10:32
* 修改人:
* 修改备注:
* 修改时间:
*/
public class MyShiroRealm extends AuthorizingRealm {
@Resource
private UserService userService;
@Resource
private ResourcesService resourcesService;
@Autowired
private RedisSessionDAO redisSessionDAO;
/**
* 方法名:
* 功能:授权
* 描述:
* 创建人:typ
* 创建时间:2018/10/9 10:35
* 修改人:
* 修改描述:
* 修改时间:
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
User user= (User) SecurityUtils.getSubject().getPrincipal();
Map map = new HashMap();
map.put("userid",user.getId());
List resourcesList = resourcesService.loadUserResources(map);
// 权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission)
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
for(Resources resources: resourcesList){
info.addStringPermission(resources.getResurl());
}
return info;
}
/**
* 方法名:
* 功能:认证
* 描述:
* 创建人:typ
* 创建时间:2018/10/9 10:35
* 修改人:
* 修改描述:
* 修改时间:
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取用户的输入的账号.
String username = (String)token.getPrincipal();
User user = userService.selectByUsername(username);
if(user==null) throw new UnknownAccountException();
if (0==user.getEnable()) {
throw new LockedAccountException(); // 帐号锁定
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user, //用户
user.getPassword(), //密码
ByteSource.Util.bytes(username),
getName() //realm name
);
// 当验证都通过后,把用户信息放在session里
Session session = SecurityUtils.getSubject().getSession();
session.setAttribute("userSession", user);
session.setAttribute("userSessionId", user.getId());
return authenticationInfo;
}
/**
* 方法名:
* 功能:根据userId 清除当前session存在的用户的权限缓存
* 描述:userIds 已经修改了权限的userId
* 创建人:typ
* 创建时间:2018/10/9 10:34
* 修改人:
* 修改描述:
* 修改时间:
*/
public void clearUserAuthByUserId(List userIds){
if(null == userIds || userIds.size() == 0) return ;
//获取所有session
Collection sessions = redisSessionDAO.getActiveSessions();
//定义返回
List list = new ArrayList();
for (Session session:sessions){
//获取session登录信息。
Object obj = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
if(null != obj && obj instanceof SimplePrincipalCollection){
//强转
SimplePrincipalCollection spc = (SimplePrincipalCollection)obj;
//判断用户,匹配用户ID。
obj = spc.getPrimaryPrincipal();
if(null != obj && obj instanceof User){
User user = (User) obj;
System.out.println("user:"+user);
//比较用户ID,符合即加入集合
if(null != user && userIds.contains(user.getId())){
list.add(spc);
}
}
}
}
RealmSecurityManager securityManager = (RealmSecurityManager) SecurityUtils.getSecurityManager();
MyShiroRealm realm = (MyShiroRealm)securityManager.getRealms().iterator().next();
for (SimplePrincipalCollection simplePrincipalCollection : list) {
realm.clearCachedAuthorizationInfo(simplePrincipalCollection);
}
}
}
具体代码不在这里赘述,相关demo见码云:
https://gitee.com/typ1805/springboot-shiro
欢迎关注