• shiro


    什么是shiro

    Apache Shiro Java 的一个安全框架Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE 环境,也可以用在JavaEE 环境。Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与Web 集成、缓存等。

    Shiro 官方网站 :http://shiro.apache.org/

     为什么要学shiro

    既然shiro将安全认证相关的功能抽取出来组成一个框架,使用shiro就可以非常快速的完成认证、授权等功能的开发,降低系统成本。

    shiro使用广泛,shiro可以运行在web应用,非web应用,集群分布式应用中越来越多的用户开始使用shiro

    基本功能:

     

    Authentication

    身份认证/登录,验证用户是不是拥有相应的身份;

    Authorization

    授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用

    户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用

    户对某个资源是否具有某个权限;

     Session Manager

    会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信

    息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;

    Cryptography加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

    Web SupportWeb 支持,可以非常容易的集成到Web 环境;

    Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;

    Concurrencyshiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能

    把权限自动传播过去;

    Testing提供测试支持;

    Run As允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;

    Remember Me记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录

    了。

     

    说明  Shiro 不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过

    相应的接口注入给Shiro即可。

    Shiro架构

     Subject

    Subject即主体,外部应用与subject进行交互,subject记录了当前操作用户,将用户的概念理解为当前操作的主体,可能是一个通过浏览器请求的用户,也可能是一个运行的程序。 Subjectshiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权

     SecurityManager 

    SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,实质上SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。

    SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。

    Authenticator

    Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,通过ModularRealmAuthenticator基本上可以满足大多数需求,也可以自定义认证器。

     Authorizer

    Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。

     realm

    Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。

    注意:不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。

     sessionManager

    sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。

     SessionDAO

    SessionDAO会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。

    CacheManager

    CacheManager即缓存管理,将用户权限数据存储在缓存,这样可以提高性能。

     Cryptography

    Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能。

    Shiro依赖包

    与其它java开源框架类似,将shirojar包加入项目就可以使用shiro提供的功能了。shiro-core是核心包必须选用,还提供了与web整合的shiro-web、与spring整合的shiro-spring、与任务调度quartz整合的shiro-quartz等,下边是shirojar包的maven坐标。

    <dependencies>
        
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>1.2.3</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-web</artifactId>
                <version>1.2.3</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>1.2.3</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-quartz</artifactId>
                <version>1.2.3</version>
            </dependency>
            
            <!-- 日志 -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.7.21</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.7.12</version>
            </dependency>
        </dependencies>

    也可以通过引入shiro-all包括shiro所有的包:

    <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-all</artifactId>
                <version>1.2.3</version>
            </dependency>

     shiro认证

    基本概念

     身份验证

    即在应用中谁能证明他就是他本人。一般提供如他们的身份ID 一些标识信息来

    表明他就是他本人,如提供身份证,用户名/密码来证明。

    shiro 中,用户需要提供principals (身份)和credentials(证明)给shiro,从而应用能

    验证用户身份:

     principals

    身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。

    一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。

    credentials

    证明/凭证,即只有主体知道的安全值,如密码/数字证书等。

    最常见的prin

    认证流程

    cipalscredentials组合就是用户名/密码了。接下来先进行一个基本的身份认证。

     

    shiro认证入门程序

    1.创建一个Maven项目

    2.配置依赖包(上面)

    3.创建日志配置文件 和 shiro.ini配置文件

    4.创建入门程序

    package cn.sxt;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.IncorrectCredentialsException;
    import org.apache.shiro.authc.UnknownAccountException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.subject.Subject;
    import org.junit.Test;
    
    public class ShiroTest {
    
        @Test
        public void testName(){
            // 1.读取shiro.ini 配置文件,创建工厂,通过工厂创建安全管理器
            IniSecurityManagerFactory managerFactory = new IniSecurityManagerFactory("classpath:shiro.ini");
            // 2.创建SecurityManager 安全管理器
            SecurityManager createInstance = managerFactory.createInstance();
            // 3.将安全管理器对象设置到当前环境中
            SecurityUtils.setSecurityManager(createInstance);
            
            //4.创建身份凭证令牌(用户登录账号密码的令牌)
            UsernamePasswordToken token = new UsernamePasswordToken("lisi","123");
            //5.创建主体对象
            Subject subject = SecurityUtils.getSubject();
            //是否认证,true 已经认证,false没有认证
            boolean authenticated = subject.isAuthenticated();
        
            System.out.println("是否已经认证:"+authenticated);
            
            if(!authenticated) {
                try {
                    //认证(登录)
                    subject.login(token);
                }catch (IncorrectCredentialsException e) {
                    System.out.println("亲。密码错误");
                }catch(UnknownAccountException e) {
                    System.out.println("亲。没有此账号");
                }
                
            }
            
            boolean authenticated1 = subject.isAuthenticated();
            System.out.println("是否已经认证:"+authenticated1);
            // 退出登录
            subject.logout();
            boolean authenticated2 = subject.isAuthenticated();
            System.out.println("是否已经认证:"+authenticated2);
        }
    }

    效果:

     

    执行流程

    1、通过ini配置文件创建securityManager

    2、调用subject.login方法主体提交认证,提交的token

    3securityManager进行认证,securityManager最终由ModularRealmAuthenticator进行认证。

    4ModularRealmAuthenticator调用IniRealm(realm传入token) ini配置文件中查询用户信息

    5IniRealm根据输入的tokenUsernamePasswordToken)从 shiro-first.ini查询用户信息,根据账号查询用户信息(账号和密码)

    如果查询到用户信息,就给ModularRealmAuthenticator返回用户信息(账号和密码)

    如果查询不到,就给ModularRealmAuthenticator返回null

    6ModularRealmAuthenticator接收IniRealm返回Authentication认证信息

    如果返回的认证信息是nullModularRealmAuthenticator抛出异常

    org.apache.shiro.authc.UnknownAccountException

    如果返回的认证信息不是null(说明inirealm找到了用户),对IniRealm返回用户密码 (在ini文件中存在)和 token中的密码 进行对比,如果不一致抛出异常

    org.apache.shiro.authc.IncorrectCredentialsException

    DisabledAccountException(帐号被禁用)

    LockedAccountException(帐号被锁定)

    ExcessiveAttemptsException(登录失败次数过多)

    ExpiredCredentialsException(凭证过期)等

     小结

    ModularRealmAuthenticator作用进行认证,需要调用realm查询用户信息(在数据库中存在用户信息)

    ModularRealmAuthenticator进行密码对比(认证过程)。

    realm:需要根据token中的身份信息去查询数据库(入门程序使用ini配置文件),如果查到用户返回认证信息,如果查询不到返回null

     自定义realm

    什么是是reaml

    Reaml 领域,范围

    通俗讲:你可以理解为一个特殊的DAO,他主要用于对认证信息和授权信息的存取和操作,实际开发中主要调用开发者开发操作数据库的相关Service获取Dao来进行授权认证

    将来实际开发需要realm从数据库中查询用户信息。

    realm接口

    自定义realm

    package cn.sxt;
    
    import java.util.Arrays;
    import java.util.List;
    
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    
    public class CustomRealm extends AuthorizingRealm{
    
        /*
         * 认证方法,开发者在方法内部自定认证的规则
         * 
         * token :令牌,在 主体login 传递过来的
         * return AuthenticationInfo
         *         返回认证信息
         *         如果返回null,认为认证失败
         * 
         */
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            /*
             * 认证思路
             *     1.获取 token令牌的身份(账号)
             *  2.在当前类中注入 UserService,调用serice的根据账号去数据库查询用户方法
             *      service调用Mapper/Dao层的根据账号查询用户方法
             *    2.1 如果没有此用户,返回null,当前认证方法也返回null
             *    2.1 如果有此用户,把用户的密码和token的凭证(密码)进行匹配
             *        2.2.1,匹配不成功,当前认证方法也返回null
             *        2.2.1  匹配成功,创建一个AuthenticationInfo 认证信息对象,认证成功
             */
            //1.获取 token令牌的身份(账号)
            String username = (String) token.getPrincipal();
            /*User user=UserService.selectByUserName(username);
             * if(user !=null){
             *进一步比对
             * }
             * 
             * */
            //模拟数据库中的账号
            List<String> usernames = Arrays.asList("list","admin","hello");
            
            if(usernames.contains(username)) {
                //身份(账号)匹配成功,进一步匹配凭证(密码)
                System.out.println("账号匹配成功");
                //String password = user.getPassword();
                //模拟数据库的密码
                String hashedCredentials="123";
                
                //.创建返回认证信息的对象
                
                SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, hashedCredentials, this.getName());
                return simpleAuthenticationInfo;
            }
    
            return null;
        }
        /*
         * 授权方法
         */
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            
            
            
            
            
            return null;
        }
    
        
    
    }

    重写配置文件

     

    记得添加依赖包

    其他与上面案例相同

     效果:

     加密算法

    上述案例我们的密码都是明文的,这样相对可能来讲对系统不是很安全,如果密码进行加密,即使是系统数据泄露,也拿不到真实的密码。

    通常需要对密码 进行散列,常用的加密方式md5sha 安全散列算法(英语:Secure Hash Algorithm,缩写为SHA

    md5密码,如果知道散列后的值可以通过穷举算法,得到md5密码对应的明文。

    建议对md5进行散列时加salt(盐),进行加密相当 于对原始密码+盐进行散列。

    正常使用时散列方法:

    在程序中对原始密码+盐进行散列,将散列值存储到数据库中,并且还要将盐也要存储在数据库中。

    如果进行密码对比时,使用相同 方法,将原始密码+盐进行散列,进行比对。

    先把密码加密:

    package cn.sxt;
    
    import org.apache.shiro.crypto.hash.Md5Hash;
    import org.apache.shiro.crypto.hash.SimpleHash;
    import org.junit.Test;
    
    public class SecurityTest {
    
        @Test
        public void testName() throws Exception {
            //原始密码
            String source="123";
            //盐每个用户的盐随机的
            String salt="sen";
            //散列次数
            int hashIterations=3;
            /*
             * source :原密码
             * sal :盐
             * hashIterations :散列次数(加密次数)
             */
            
            //加密方式一
            Md5Hash md5Hash = new Md5Hash(source,salt,hashIterations);
            System.out.println(md5Hash);
            //加密方式 : md5,md2,sha1
            String algorithmName = "md5";
            //方式二
            SimpleHash simpleHash=new SimpleHash(algorithmName, source, salt,hashIterations);
            
            System.out.println(simpleHash);
    
        }
    }

    加密后:5585e2acfff82f34259391863c714c45

    把解码方式配置给realm:

    #配置自定义realm
    #类似于 spring 的 <bean id="" class="">
    #自定义realm名称=自定义realm的全限定名
    customRealm=cn.sxt.CustomRealm
    
    #定义凭证器
    credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
    #算法
    credentialsMatcher.hashAlgorithmName=md5
    #散列次数
    credentialsMatcher.hashIterations=3
    #设置realm的凭证匹配器
    customRealm.credentialsMatcher=$credentialsMatcher
    
    #将自定义realm设置给SecurityManager的realm属性(类型Spring的依赖注入)
    securityManager.realms=$customRealm

    认证:

    package cn.sxt;
    
    import java.util.Arrays;
    import java.util.List;
    
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.util.ByteSource;
    
    public class CustomRealm extends AuthorizingRealm{
    
        /*
         * 认证方法,开发者在方法内部自定认证的规则
         * 
         * token :令牌,在 主体login 传递过来的
         * return AuthenticationInfo
         *         返回认证信息
         *         如果返回null,认为认证失败
         * 
         */
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            /*
             * 认证思路
             *     1.获取 token令牌的身份(账号)
             *  2.在当前类中注入 UserService,调用serice的根据账号去数据库查询用户方法
             *      service调用Mapper/Dao层的根据账号查询用户方法
             *    2.1 如果没有此用户,返回null,当前认证方法也返回null
             *    2.1 如果有此用户,把用户的密码和token的凭证(密码)进行匹配
             *        2.2.1,匹配不成功,当前认证方法也返回null
             *        2.2.1  匹配成功,创建一个AuthenticationInfo 认证信息对象,认证成功
             */
            //1.获取 token令牌的身份(账号)
            String username = (String) token.getPrincipal();
            /*User user=UserService.selectByUserName(username);
             * if(user !=null){
             *进一步比对
             * }
             * 
             * */
            //模拟数据库中的账号
            List<String> usernames = Arrays.asList("list","admin","hello");
            
            if(usernames.contains(username)) {
                //身份(账号)匹配成功,进一步匹配凭证(密码)
                System.out.println("账号匹配成功");
                //String password = user.getPassword();
                //(模拟数据库中的密码) abc + sen +散列三次后的密码
                String hashedCredentials="5585e2acfff82f34259391863c714c45";
                //数据库中的盐
                ByteSource credentialsSalt = ByteSource.Util.bytes("sen");
                //.创建返回认证信息的对象
                
                SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, hashedCredentials, credentialsSalt,this.getName());
                return simpleAuthenticationInfo;
            }
    
            return null;
        }
        /*
         * 授权方法
         */
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            
            
            
            
            
            return null;
        }
    
        
    
    }

    shiro授权

    授权,也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作

    等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、

    角色(Role)。

    关键对象介绍

    主体

    主体,即访问应用的用户,在Shiro中使用Subject代表该用户。用户只有授权后才允许访

    问相应的资源。

    资源

    在应用中用户可以访问的任何东西,比如访问JSP 页面、查看/编辑某些数据、访问某个业

    务方法、打印文本等等都是资源。用户只要授权后才能访问。

    权限

    安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的

    权力。即权限表示在应用中用户能不能访问某个资源,如:访问用户列表页面查看/新增/修改/删除用户数据(即很多时候都是CRUD(增查改删)式权限控制)打印文档等等。。。

    角色

    角色代表了操作集合,可以理解为权限的集合,一般情况下我们会赋予用户角色而不是权

    限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总

    监、CTO、开发工程师等都是角色,不同的角色拥有一组不同的权限。

    授权流程

     

    Shiro 支持三种方式的授权:

    编程式:通过写if/else 授权代码块完成

    Subject subject = SecurityUtils.getSubject();

    if(subject.hasRole("admin")) {

    //有权限

    } else {

    //无权限

    }

     

     

    注解式:通过在执行的Java方法上放置相应的注解完成

    @RequiresPermissions("admin:list")

    public void list() {

    System.out.println("有权限才能执行此方法");

    }

     JSP 标签:在JSP 页面通过相应的标签完成

    <shiro:hasRole name="admin">

    执行代码

    </shiro:hasRole>

    shiro.ini

    shiro.ini里边的内容相当于在数据库。

    #配置账号
    [users]
    
    #账号=密码,角色1
    zhangsan=abc,role1
    list=123,role2,role1
    #配置角色
    [roles]
    #角色= 多个角色使用逗号隔开
    role1=user:list,user:delete,user:create,user:update
    #role=user:*
    role2=dept:list,user:inse

    程序编写

    package cn.sxt;
    
    import java.util.Arrays;
    import java.util.List;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.IncorrectCredentialsException;
    import org.apache.shiro.authc.UnknownAccountException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.subject.Subject;
    import org.junit.Test;
    
    public class ShiroTest {
    
        @Test
        public void testName(){
            // 1.读取shiro.ini 配置文件,创建工厂,通过工厂创建安全管理器
            IniSecurityManagerFactory managerFactory = new IniSecurityManagerFactory("classpath:shiro.ini");
            // 2.创建SecurityManager 安全管理器
            SecurityManager createInstance = managerFactory.createInstance();
            // 3.将安全管理器对象设置到当前环境中
            SecurityUtils.setSecurityManager(createInstance);
            
            //4.创建身份凭证令牌(用户登录账号密码的令牌)
            UsernamePasswordToken token = new UsernamePasswordToken("list","123");
            //5.创建主体对象
            Subject subject = SecurityUtils.getSubject();
            //是否认证,true 已经认证,false没有认证
            boolean authenticated = subject.isAuthenticated();
        
            System.out.println("是否已经认证:"+authenticated);
            
            if(!authenticated) {
                try {
                    //认证(登录)
                    subject.login(token);
                }catch (IncorrectCredentialsException e) {
                    System.out.println("亲。密码错误");
                }catch(UnknownAccountException e) {
                    System.out.println("亲。没有此账号");
                }
                
            }
            
            boolean authenticated1 = subject.isAuthenticated();
            System.out.println("是否已经认证:"+authenticated1);
            // 退出
            //subject.logout();
            boolean authenticated2 = subject.isAuthenticated();
            System.out.println("是否已经认证:"+authenticated2);
            //1. 判断是否有单个角色
            System.out.println(subject.hasRole("role3"));
            //2.判断是否有多个角色
            List<String> results = Arrays.asList("role1","role2");
          
            boolean[] hasRoles = subject.hasRoles(results);
            //--------------------------------------
            System.out.println("result[0] :"+hasRoles[0]);
            System.out.println("result[1] :"+hasRoles[1]);
            
            //--------------------------------------
            //2.判断是否有权限
            boolean permitted = subject.isPermitted("user:select");
              System.out.println(permitted);
            //3.判断是否有多个权限
              boolean permittedAll = subject.isPermittedAll("user:list","user:update");
              System.out.println(permittedAll);
        }
    }

     自定义realm进行授权

     需求

    上边的程序通过shiro-permission.ini对权限信息进行静态配置,实际开发中从数据库中获取权限数据。就需要自定义realm,由realm从数据库查询权限数据。

    realm根据用户身份查询权限数据,将权限数据返回给authorizer(授权器)。

    自定义realm

    在原来自定义的realm中,修改doGetAuthorizationInfo方法。

    #配置自定义realm
    #类似于 spring 的 <bean id="" class="">
    #自定义realm名称=自定义realm的全限定名
    customRealm=cn.sxt.CustomRealm
    
    #定义凭证器
    credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
    #算法
    credentialsMatcher.hashAlgorithmName=md5
    #散列次数
    credentialsMatcher.hashIterations=3
    #设置realm的凭证匹配器
    customRealm.credentialsMatcher=$credentialsMatcher
    
    #将自定义realm设置给SecurityManager的realm属性(类型Spring的依赖注入)
    securityManager.realms=$customRealm
    package cn.sxt;
    
    import java.util.Arrays;
    import java.util.List;
    
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.util.ByteSource;
    
    public class CustomRealm extends AuthorizingRealm{
    
        /*
         * 认证方法,开发者在方法内部自定认证的规则
         * 
         * token :令牌,在 主体login 传递过来的
         * return AuthenticationInfo
         *         返回认证信息
         *         如果返回null,认为认证失败
         * 
         */
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            /*
             * 认证思路
             *     1.获取 token令牌的身份(账号)
             *  2.在当前类中注入 UserService,调用serice的根据账号去数据库查询用户方法
             *      service调用Mapper/Dao层的根据账号查询用户方法
             *    2.1 如果没有此用户,返回null,当前认证方法也返回null
             *    2.1 如果有此用户,把用户的密码和token的凭证(密码)进行匹配
             *        2.2.1,匹配不成功,当前认证方法也返回null
             *        2.2.1  匹配成功,创建一个AuthenticationInfo 认证信息对象,认证成功
             */
            //1.获取 token令牌的身份(账号)
            String username = (String) token.getPrincipal();
            /*User user=UserService.selectByUserName(username);
             * if(user !=null){
             *进一步比对
             * }
             * 
             * */
            //模拟数据库中的账号
            List<String> usernames = Arrays.asList("list","admin","hello");
            
            if(usernames.contains(username)) {
                //身份(账号)匹配成功,进一步匹配凭证(密码)
                System.out.println("账号匹配成功");
                //String password = user.getPassword();
                //(模拟数据库中的密码) abc + sen +散列三次后的密码
                String hashedCredentials="5585e2acfff82f34259391863c714c45";
                //数据库中的盐
                ByteSource credentialsSalt = ByteSource.Util.bytes("sen");
                //.创建返回认证信息的对象
                
                SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, hashedCredentials, credentialsSalt,this.getName());
                return simpleAuthenticationInfo;
            }
    
            return null;
        }
        /*
         * 授权方法
         */
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            
            Object username = principals.getPrimaryPrincipal();
            System.out.println(username);
            /*
             * 授权思路
             *     1.通过当前认证的身份去数据库里面查询出当前身份对应的角色--》对应的权限
             *         注入RoleService
             *         Role role = roleService.selectByPrimarykey(roleId); 
             *         role.permissionIds = 10,1,13,15,16,17,11,18,19,20,21,12,22,23,24,25
             *         List<String> permissionExpressions = permission.selectExpressionsByIds(权限id数组集合)
             *         例如
             *             user:lsit
             *             user:create 
             *             student:list
             *             等等
             *  2. 将当前身份 对应的角色对应的所有权限设置给Shiro 授权信息对象
             *  3. 程序运行shiro会自动判断当前身份是否有权限
             */
            //模拟数据库查询权限
            List<String> asList = Arrays.asList("user:list","user:insert");
            //创建一个授权信息对象
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            //将权限添加到shiro 授权信息对象:不能为空null
            simpleAuthorizationInfo.addStringPermissions(asList);
            System.out.println("CustomRealm.doGetAuthorizationInfo()");
            return simpleAuthorizationInfo;
        }
    
        
    
    }
    package cn.sxt;
    
    import java.util.Arrays;
    import java.util.List;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.IncorrectCredentialsException;
    import org.apache.shiro.authc.UnknownAccountException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.subject.Subject;
    import org.junit.Test;
    public class ShiroTest {
    
        @Test
        public void testName(){
            // 1.读取shiro.ini 配置文件,创建工厂,通过工厂创建安全管理器
            IniSecurityManagerFactory managerFactory = new IniSecurityManagerFactory("classpath:shiro.ini");
            // 2.创建SecurityManager 安全管理器
            SecurityManager createInstance = managerFactory.createInstance();
            // 3.将安全管理器对象设置到当前环境中
            SecurityUtils.setSecurityManager(createInstance);
            
            //4.创建身份凭证令牌(用户登录账号密码的令牌)
            UsernamePasswordToken token = new UsernamePasswordToken("list","123");
            //5.创建主体对象
            Subject subject = SecurityUtils.getSubject();
            //是否认证,true 已经认证,false没有认证
            boolean authenticated = subject.isAuthenticated();
        
            System.out.println("是否已经认证:"+authenticated);
            
            if(!authenticated) {
                try {
                    //认证(登录)
                    subject.login(token);
                }catch (IncorrectCredentialsException e) {
                    System.out.println("亲。密码错误");
                }catch(UnknownAccountException e) {
                    System.out.println("亲。没有此账号");
                }
                
            }
            
            boolean authenticated1 = subject.isAuthenticated();
            System.out.println("是否已经认证:"+authenticated1);
            // 退出
            //subject.logout();
            boolean authenticated2 = subject.isAuthenticated();
            System.out.println("是否已经认证:"+authenticated2);
            
            //--------------------------------------
            //.判断是否有权限
            boolean permitted = subject.isPermitted("user:select");
              System.out.println(permitted);
            //.判断是否有多个权限
              boolean permittedAll = subject.isPermittedAll("user:list","user:insert");
              System.out.println(permittedAll);
        }
    }

  • 相关阅读:
    代码发布概述图
    gitPython模块
    django重点面试题
    paramiko模块
    Spring Security 注解
    JsonIgnoreProperties JsonIgnore导致RequestBody无法接受参数值
    Http和Rpc区别
    ExceptionHandler(思路参考CustomException)
    Linux安装Nginx
    秒杀扣除库存方案
  • 原文地址:https://www.cnblogs.com/406070989senlin/p/11218992.html
Copyright © 2020-2023  润新知