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;
   }
                

















