springboot整合shiro
依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.9.0</version>
</dependency>
登录
自定义realm
@Component
public class MyRealm extends AuthorizingRealm { //AuthorizingRealm 继承于AuthenticatingRealm
@Autowired
private UserService userService;
//自定义授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
//自定义认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取用户身份信息
String username = token.getPrincipal().toString();
//调用业务层获取用户信息
User user = userService.getByName(username);
//校验
if (user != null){
AuthenticationInfo info = new SimpleAuthenticationInfo(
token.getPrincipal(),
user.getPassword(),
ByteSource.Util.bytes("盐值"),
username
);
return info;
}
return null;
}
}
SimpleAuthenticationInfo构造参数:
- 保存的用户凭证,可以是用户id、用户名甚至是用户对象(需要序列化),可以在subject.getPrincipal()、principalCollection.getPrimaryPrincipal()中进行获取
- 数据库中获取的密码,与token中的密码进行对比,匹配上了就通过,否则报异常
- 密码加密用的盐值,可选
- 当前realm的名字
配置类型
@Configuration
public class ShiroConfig {
@Autowired
private MyRealm myRealm;
//配置SecurityManager
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(){
//创建SecurityManager对象
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
//创建加密对象,设置相关属性
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5");//算法名称
matcher.setHashIterations(3);//迭代次数
//将加密对象存储到myRealm种
myRealm.setCredentialsMatcher(matcher);
//将myRealm存入manager种
manager.setRealm(myRealm);
return manager;
}
//拦截范围
@Bean
public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
//设置不认证可访问的资源
definition.addPathDefinition("/login","anon");
//设置需要进行登录认证的拦截范围
definition.addPathDefinition("/**","authc");
return definition;
}
}
登录接口
@GetMapping("login")
public String login(String name,String pwd){
//获取登录认证对象
Subject subject = SecurityUtils.getSubject();
//封装请求数据到token
AuthenticationToken token = new UsernamePasswordToken(name,pwd);
try {
//调用login方法进行登录认证
subject.login(token);
return "登录成功";
}catch (AuthenticationException e){
e.printStackTrace();
return "登录失败";
}
}
多realm
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(){
//创建对象
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
//多realm策略
ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
authenticator.setAuthenticationStrategy(new AllSuccessfulStrategy());//具体使用的策略
manager.setAuthenticator(authenticator);//要在setRealms之前设置
//创建加密对象,设置相关属性
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5");//算法名称
matcher.setHashIterations(3);//迭代次数
//将加密对象存储到myRealm种
myRealm.setCredentialsMatcher(matcher);
myRealm2.setCredentialsMatcher(matcher);
//将myRealm存入manager种
manager.setRealms(Arrays.asList(myRealm,myRealm2));//按照先后顺序执行
return manager;
}
具体策略:
- AllSuccessfulStrategy:所有都满足
- AtLeastOneSuccessfulStrategy:至少满足一个(默认)
- FirstSuccessfulStrategy:满足一个即可
记住我
修改配置文件
@Configuration
public class ShiroConfig {
@Autowired
private MyRealm myRealm;
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(){
//......
//记住我
manager.setRememberMeManager(rememberMeManager());
return manager;
}
//cookie属性设置
public SimpleCookie rememberMeCookie(){
SimpleCookie cookie = new SimpleCookie("cookieName");
cookie.setPath("/");
cookie.setHttpOnly(true);
cookie.setMaxAge(30*24*60*60);
return cookie;
}
//cookie管理对象
private RememberMeManager rememberMeManager() {
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
//设置cookie
cookieRememberMeManager.setCookie(rememberMeCookie());
return cookieRememberMeManager;
}
//拦截范围
@Bean
public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
//设置不认证可访问的资源
definition.addPathDefinition("/login","anon");
//设置需要进行登录认证的拦截范围
definition.addPathDefinition("/**","user");//记住我需要使用user
return definition;
}
}
修改登录接口
添加rememberMe参数
@GetMapping("login")
public String login(@RequestParam String name,@RequestParam String pwd,@RequestParam(defaultValue = "false") Boolean rememberMe){
//获取登录认证对象
Subject subject = SecurityUtils.getSubject();
//封装请求数据到token
AuthenticationToken token = new UsernamePasswordToken(name,pwd,rememberMe);
try {
//调用login方法进行登录认证
subject.login(token);
return "登录成功";
}catch (AuthenticationException e){
e.printStackTrace();
return "登录失败";
}
}
登出
修改拦截配置
//拦截范围
@Bean
public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
//设置不认证可访问的资源
definition.addPathDefinition("/login","anon");
//配置登出过滤器
definition.addPathDefinition("/logout","logout");//注意拦截器的顺序
//设置需要进行登录认证的拦截范围
//definition.addPathDefinition("/**","authc");
definition.addPathDefinition("/**","user");
return definition;
}
授权
修改Realm
@Component
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
//自定义授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//创建对象,封装当前登录用户的角色权限信息
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//获取用户身份信息
String username = principals.getPrimaryPrincipal().toString();
//存储角色
List<String> roleCodeList = userService.getRoleCodeByUsername(username);
info.addRoles(roleCodeList);
//存储权限
List<String> menuCodeList = userService.getMenuCodeByUsername(username);
info.addStringPermissions(menuCodeList);
//返回信息
return info;
}
//自定义认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//......
}
}
在接口上添加注解进行验证
注解参考:shiro-注解 | 路人丁 (dingqinan.com)
缓存
整合EhCache
导入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.2</version>
</dependency>
在resources下添加配置文件
ehcache/ehcache-shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--磁盘的缓存位置-->
<diskStore path="java.io.tmpdir"/>
<!--默认缓存-->
<defaultCache maxEntriesLocalHeap="1000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="3600"
overflowToDisk="false"/>
<!-- 登录认证信息缓存:缓存用户的角色和权限 -->
<cache name="loginRoleMenuCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true"/>
</ehcache>
修改配置类
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(){
//......
//设置缓存
manager.setCacheManager(getEhCacheManager());
return manager;
}
//创建缓存管理器
@SneakyThrows
private EhCacheManager getEhCacheManager() {
EhCacheManager ehCacheManager = new EhCacheManager();
InputStream is = ResourceUtils.getInputStreamForPath("classpath:ehcache/ehcache-shiro.xml");
CacheManager cacheManager = new CacheManager(is);
ehCacheManager.setCacheManager(cacheManager);
return ehCacheManager;
}
整合redis
导入依赖
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis-spring-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
修改配置文件
@Autowired
private RedisSessionDAO redisSessionDAO;//shiro-redis提供的
@Autowired
private RedisCacheManager redisCacheManager;//shiro-redis提供的
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(){
//......
//设置缓存
manager.setSessionManager(sessionManager());
manager.setCacheManager(redisCacheManager);
return manager;
}
@Bean
public SessionManager sessionManager(){
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO);
return sessionManager;
}
会话管理
SessionManager会话管理器,负责创建和管理用户的会话(session)生命周期,它能够在任何环境中在本地管理用户会话,即使没有web/servlet/ejb容器,也一样可以保存会话
默认情况下,shiro会检测当前环境中现有的会话机制进行适配,如果没有,它会使用内置的会话管理器来提供会话管理
SessionDAO负责Session的持久化操作,允许session数据写入到后端持久化数据库
SessionManager由SecurityManager管理,shiro提供了三种实现
实现 | 说明 |
---|---|
DefaultSessionManager | 用于javese环境 |
ServletContainerSessionManager | 用于web环境,直接使用servlet容器的会话 |
DefaultWebSessionManager | 用于web环境,自己维护会话 |
操作
//获取session对象
Session session = SecurityUtils.getSubject().getSession();
//设置
session.setAttribute("key","value");
//获取
session.getAttribute("key");
controller中的request,在shiro过滤器中的doFilerInternal方法,被包装成ShiroHttpServletRequest
SecurityManager和SessionManager会话管理器决定session来源于ServletRequest还是由shiro管理的会话
无论是通过request.getSession还是subject.getSession获取到的session,两者是等价的
异常
参考:shiro-异常 | 路人丁 (dingqinan.com)