• 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才开始有的,无语了。

  • 相关阅读:
    让x86的android模拟器能模拟arm架构系统
    婴儿补充微量元素
    asterisk 能打电话的配置
    SIP协议错误代码大全
    asterisk错误排查
    asterisk帮助与国内论坛
    win10 只要打开文件对话框就卡死解决方法
    分享到朋友圈实现
    跳转前暂停几秒js如何实现
    Github css加载失败,样式混乱解决办法
  • 原文地址:https://www.cnblogs.com/lihaoyang/p/6618716.html
Copyright © 2020-2023  润新知