Shiro实战详解(3)
- 04 Springboot集成Shiro
- 1、技术栈
- 2、数据库设计
- 3、注解方式鉴权
- 05 实现分布式会话SessionManager
- 1、会话的问题
- 2、分布式会话实现思路
- 3、实现步骤:
- 1.创建RedisSessionDao extends AbstractSessionDAO
- 2.配置ShiroConfig
04 Springboot集成Shiro
1、技术栈
主框架:springboot
响应层:springMVC
持久层:mybatis
事务控制:jta
前端技术:vue+ElementUI
2、数据库设计
sh_user:用户表,一个用户可以有多个角色
sh_role:角色表,一个角色可以有多个资源
sh_resource:资源表
sh_user_role:用户角色中间表
sh_role_resource:角色资源中间表
3、注解方式鉴权
以下为常用注解
注解 | 说明 |
---|---|
@RequiresAuthentication | 表明当前用户需是经过认证的用户 |
@ RequiresGuest | 表明该用户需为”guest”用户 |
@RequiresPermissions | 当前用户需拥有指定权限 |
@RequiresRoles | 当前用户需拥有指定角色 |
@ RequiresUser | 当前用户需为已认证用户或已记住用户 |
05 实现分布式会话SessionManager
1、会话的问题
在使用多个服务器,实现分布式服务时,用户在登录服务器已经登录,但是其他服务器对用户来说由于第一次访问,没有用户的会话信息,就会拦截,让他去登录,但实际上我们已经登录过了。
2、分布式会话实现思路
针对出现的session会话问题,可以使用redis来存储session会话信息,来实现共享session。
所有服务器的session信息都存储到了同一个Redis集群中,即所有的服务都将 Session 的信息存储到 Redis 集群中,无论是对 Session 的注销、更新都会同步到集群中,达到了 Session 共享的目的。
Cookie 保存在客户端浏览器中,而 Session 保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上,这就是 Session。客户端浏览器再次访问时只需要从该 Session 中查找该客户的状态就可以了。
在实际工作中我们建议使用外部的缓存设备(包括Redis)来共享 Session,避免单个服务器节点挂掉而影响服务,共享数据都会放到外部缓存容器中
3、实现步骤:
1.创建RedisSessionDao extends AbstractSessionDAO
public class RedisSessionDao extends AbstractSessionDAO {
@Autowired(required = false)
RedisTemplate redisTemplate;
@Override
protected Serializable doCreate(Session session) {
//获取sessionid
Serializable id = generateSessionId(session); //传入参数获取id
//调用父类方法
assignSessionId(session,id);
redisTemplate.opsForValue().set(id,session);
return id;
}
@Override
protected Session doReadSession(Serializable serializable) {
return (Session) redisTemplate.opsForValue().get(serializable);
}
@Override
public void delete(Session session) {
redisTemplate.delete(session.getId());
}
}
2.配置ShiroConfig
/**
* 1.创建shiro自带cookie对象
*/
@Bean
public SimpleCookie sessionIdCookie(){
SimpleCookie simpleCookie = new SimpleCookie();
simpleCookie.setName("ShiroSession");
return simpleCookie;
}
//2.创建realm
@Bean
public MyRealm getRealm() {
return new MyRealm();
}
/*
* 注入创建的RedisSessionDao类
* */
@Bean
public RedisSessionDao getRedisSessionDao(){
return new RedisSessionDao();
}
/**
* 3.创建会话管理器
*/
@Bean
public DefaultWebSessionManager sessionManager(){
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(getRedisSessionDao());
sessionManager.setSessionValidationSchedulerEnabled(false);
sessionManager.setSessionIdCookieEnabled(true);
sessionManager.setSessionIdCookie(sessionIdCookie());
sessionManager.setGlobalSessionTimeout(3600000);
return sessionManager;
}
//4.创建安全管理器
@Bean
public SecurityManager defaultWebSecurityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(getRealm());
securityManager.setSessionManager(sessionManager());
return securityManager;
}
/**
* 5.保证实现了Shiro内部lifecycle函数的bean执行
*/
@Bean(name = "lifecycleBeanPostProcessor")
public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 6.开启对shiro注解的支持
* AOP式方法级权限检查
*/
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
/**
* 7.配合DefaultAdvisorAutoProxyCreator事项注解权限校验
*/
@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager());
return authorizationAttributeSourceAdvisor;
}
//8.配置shiro的过滤器工厂再web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制
@Bean
public ShiroFilterFactoryBean myShiroFilterFactoryBean(){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setLoginUrl("/usererror"); //认证失败去这里
factoryBean.setSecurityManager(defaultWebSecurityManager());
Map<String, String> map = new LinkedHashMap<>();
//认证页面
map.put("/login","anon"); //匿名
map.put("/user/**","authc"); //认证后
factoryBean.setFilterChainDefinitionMap(map);
return factoryBean;
}