shiro 介绍和使用

浪子不回头ぞ 提交于 2020-08-09 12:20:49

 

一、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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!