• shiro学习笔记_0400_自定义realm实现身份认证


     自定义Realm实现身份认证

    先来看下Realm的类继承关系:

    Realm接口有三个方法,最重要的是第三个方法:

    a) String getName():返回此realm的名字

    b) boolean supports(AuthenticationToken token) :好像是说,判断是哪个类型的token

    c)* AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException:获得认证信息

    ===========================================================================

    1,jdbcRealm已经实现了从数据库中获取用户的验证信息,但是jdbcRealm灵活性太差。如果要实现自己的一些特殊应用时将不能支持。此时可通过自定义Realm来实现身份的认证功能。

    2,在Realm接口中,在接口中最重要的是方法是getAuthenticationInfo(token) ,可以根据token获得认证信息。

        由上图可知,Shiro内容实现了一系列的realm。这些不同的Realm实现类提供了不同的功能,最重要的是:

         a)AuthenticatingRealm:AuthenticationInfo getAuthenticationInfo(AuthenticationToken token)实现了获取身份信息的功能

         b)AuthorizingRealm:AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals)实现了获取权限信息的功能。

    由于AuthorizingRealm是AuthenticatingRealm的子类,所以,通常自定义Realm需要继承AuthorizingRealm,这样既可以提供了身份认证的自定义方法,也可以实现授权的自定义方法。

    ====================================================================================

    实验:

    1,新建maven项目,添加依赖:

     <dependencies>  
          <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId> 
            <version>1.3.2</version> 
        </dependency> 
        
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</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.5</version>
        </dependency> 
         
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
        
        <dependency> 
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.31</version>
        </dependency>

    2,项目结构

    先来说说散列算法吧,因为里边要用到shiro提供的加密算法,以MD5为例。shiro提供了好多个散列加密算法:(此段有待查阅资料学习补充)

    Md5例子,shiro加密可以加盐,还可以迭代多次进行加密,就是加密之后在加密。

    public class Md5Demo {
    
        public static void main(String[] args) {
            String pwd = "1111";
            
            //使用md5加密
            Md5Hash md5 = new Md5Hash(pwd);
    //        System.out.println("123进行md5加密----"+md5.toString());
            //加盐
            md5 = new Md5Hash(pwd,"lhy");
    //        System.out.println("1111 md5加密加盐---"+md5.toString());
            //迭代加密
            md5 = new Md5Hash(pwd, "lhy", 2); 
            System.out.println("123 md5加盐迭代---"+md5.toString());
            
        
        }
    }

     3,数据库表:

    4,ini配置文件:

    [main]
    #定义凭证匹配器
    credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
    #设置散列算法
    credentialsMatcher.hashAlgorithmName=md5
    #设置散列次数
    credentialsMatcher.hashIterations=2
    userRealm=com.lhy.realm.UserRealm
    #将凭证匹配器设置到realm
    userRealm.credentialsMatcher=$credentialsMatcher
    securityManager.realm=$userRealm

    自定义userRealm继承了AuthorizingRealm,AuthorizingRealm 继承了AuthenticatingRealm,在AuthenticatingRealm中,有setCredentialsMatcher(matcher);方法,

    用来设置凭证的匹配器。凭证匹配器CredentialsMatcher是个接口,我们用其实现类HashedCredentialsMatcher。HashedCredentialsMatcher中有setHashAlgorithmName 方法用来设置算法名字,setHashIterations(int hashIterations)方法用来设置加密的迭代次数(看源码可知)。

    log4j:

    log4j.rootLogger=info, stdout
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n

    5,userRealm类:

    /**
     * 自定义Realm实现,该Realm类提供了两个方法:
     * 1,doGetAuthenticationInfo:获取认证信息
     * 2,doGetAuthorizationInfo:获取权限信息
     * @author Administrator
     *
     */
    public class UserRealm extends AuthorizingRealm{
    
        //realm接口的方法
        @Override
        public String getName() {
            return "userRealm";
        }
        
        //完成身份认证并返回认证信息
        //如果身份认证失败,返回null
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(
                AuthenticationToken token) throws AuthenticationException {
            //获取用户输入的用户名
            String username = (String)token.getPrincipal();//获取身份信息
            System.out.println("username----"+username);
            //实际开发是调用service根据用户名去数据库查对应的密码
            //假设获取的密码是1111
    //        String password = "1111";
            String password ="cda480d3c0ffa424905444e760e7447d";
            String salt = "lhy";
            
            //将数据库中获取的进行封装,注意盐值的类型是ByteSource
            //参数:用户名,加密后的密码,盐值,realm名字
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password, ByteSource.Util.bytes(salt), getName());
            return info;
        } 
        
        //授权的信息
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(
                PrincipalCollection principals) {
            
            return null;
        }
    
    
    }

    测试程序,步骤有点简单,上述代码中的password是由字符串"niubei" 根据盐lhy 迭代2次后的结果,用来模拟从数据库中取出的加密后的密码,

    测试代码:

    public class userRealmDemo { 
    
        public static void main(String[] args) {
            //1,创建SecurityManager工厂  读取shiro配置文件
            Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
            //2  通过securityManager工厂获取SecurityManager实例
            SecurityManager securityManager = factory.getInstance();
            //3 将SecurityManager 对象设置到运行环境
            SecurityUtils.setSecurityManager(securityManager);
            //4 通过SecurityUtils获取主体subject
            Subject subject = SecurityUtils.getSubject();
            try {
                //5.假设登录名是zhangsan  密码是1111
                UsernamePasswordToken token = new UsernamePasswordToken("niubei","1111");
                //6,登录,进行用户身验证 
                subject.login(token);
                //通过subject判断用户是否通过验证
                if(subject.isAuthenticated()){
                    System.out.println("用户登录成功!"); 
                }
            } catch (UnknownAccountException e) {     
                System.out.println("用户名或密码错误!"); 
                e.printStackTrace();
            }catch (IncorrectCredentialsException e) {
                System.out.println("用户名或密码错误!");
                e.printStackTrace();
            }
            //其他异常处理。。。
            //7 退出
            subject.logout();   
        }
    }

    输出:

    username----niubei
    2017-03-26 19:02:04,487 INFO [org.apache.shiro.session.mgt.AbstractValidatingSessionManager] - Enabling session validation scheduler... 
    用户登录成功!

    测试成功。。。

    注意,刚开始我用的shiro版本是1.1.0,userRealm代码中 ByteSource.Util.bytes(salt) 报错,ByteSource接口根本没有Util类,百度了一下,看了这个接口的源代码才知道,这个Util类从shiro的1.2才开始有的,无语了。

  • 相关阅读:
    3.Appium运行时出现:Original error: Android devices must be of API level 17 or higher. Please change your device to Selendroid or upgrade Android on your device
    3.Python连接数据库PyMySQL
    2.Python输入pip命令出现Unknown or unsupported command 'install'问题解决
    2.Linux下安装Jenkins
    5.JMeter测试mysql数据库
    Android 4学习(7):用户界面
    Android 4学习(6):概述
    Android 4学习(5):概述
    Android 4学习(4):概述
    Android 4学习(3):概述
  • 原文地址:https://www.cnblogs.com/lihaoyang/p/6618716.html
Copyright © 2020-2023  润新知