zoukankan      html  css  js  c++  java
  • shrio 身份认证流程-Realm

    身份认证流程

    流程如下:

    1、首先调用Subject.login(token)进行登录,其会自动委托给Security Manager,调用之前必须通过SecurityUtils. setSecurityManager()设置;

    2、SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证;

    3、Authenticator才是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现;

    4、Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证,默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多Realm身份验证;

    5、Authenticator会把相应的token传入Realm,从Realm获取身份验证信息,如果没有返回/抛出异常表示身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。

    Realm

    Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。

    org.apache.shiro.realm.Realm接口如下

    String getName(); //返回一个唯一的Realm名字
    boolean supports(AuthenticationToken token); //判断此Realm是否支持此Token
    AuthenticationInfo getAuthenticationInfo(AuthenticationToken token)
       throws AuthenticationException;  //根据Token获取认证信息
    

      

    Realm配置

    1、自定义Realm实现(com.github.zhangkaitao.shiro.chapter2.realm.MyRealm1):  

    public class MyRealm1 implements Realm {
        @Override
        public String getName() {
            return "myrealm1";
        }
        @Override
        public boolean supports(AuthenticationToken token) {
            //仅支持UsernamePasswordToken类型的Token
            return token instanceof UsernamePasswordToken; 
        }
        @Override
        public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            String username = (String)token.getPrincipal();  //得到用户名
            String password = new String((char[])token.getCredentials()); //得到密码
            if(!"zhang".equals(username)) {
                throw new UnknownAccountException(); //如果用户名错误
            }
            if(!"123".equals(password)) {
                throw new IncorrectCredentialsException(); //如果密码错误
            }
            //如果身份认证验证成功,返回一个AuthenticationInfo实现;
            return new SimpleAuthenticationInfo(username, password, getName());
        }
    } 
    

    2、ini配置文件指定自定义Realm实现(shiro-realm.ini)  

    #声明一个realm
    myRealm1=com.github.zhangkaitao.shiro.chapter2.realm.MyRealm1
    #指定securityManager的realms实现
    securityManager.realms=$myRealm1 
    

    通过$name来引入之前的realm定义

    Realm配置

    1、ini配置文件(shiro-multi-realm.ini)  

    #声明一个realm
    myRealm1=com.github.zhangkaitao.shiro.chapter2.realm.MyRealm1
    myRealm2=com.github.zhangkaitao.shiro.chapter2.realm.MyRealm2
    #指定securityManager的realms实现
    securityManager.realms=$myRealm1,$myRealm2 
    

      securityManager会按照realms指定的顺序进行身份认证。此处我们使用显示指定顺序的方式指定了Realm的顺序,如果删除“securityManager.realms=$myRealm1,$myRealm2”,那么securityManager会按照realm声明的顺序进行使用(即无需设置realms属性,其会自动发现),当我们显示指定realm后,其他没有指定realm将被忽略,如“securityManager.realms=$myRealm1”,那么myRealm2不会被自动设置进去。

    Shiro默认提供的Realm

    以后一般继承AuthorizingRealm(授权)即可;其继承了AuthenticatingRealm(即身份验证),而且也间接继承了CachingRealm(带有缓存实现)。其中主要默认实现如下:

    org.apache.shiro.realm.text.IniRealm[users]部分指定用户名/密码及其角色;[roles]部分指定角色即权限信息;

    org.apache.shiro.realm.text.PropertiesRealm user.username=password,role1,role2指定用户名/密码及其角色;role.role1=permission1,permission2指定角色及权限信息;

    org.apache.shiro.realm.jdbc.JdbcRealm通过sql查询相应的信息,如“select password from users where username = ?”获取用户密码,“select password, password_salt from users where username = ?”获取用户密码及盐;“select role_name from user_roles where username = ?”获取用户角色;“select permission from roles_permissions where role_name = ?”获取角色对应的权限信息;也可以调用相应的api进行自定义sql;

    JDBC Realm使用

    1、数据库及依赖

    使用mysql数据库及druid连接池; 

    2、到数据库shiro下建三张表:users(用户名/密码)、user_roles(用户/角色)、roles_permissions(角色/权限);并添加一个用户记录,用户名/密码为zhang/123;

    3、ini配置(shiro-jdbc-realm.ini) 

    jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
    dataSource=com.alibaba.druid.pool.DruidDataSource
    dataSource.driverClassName=com.mysql.jdbc.Driver
    dataSource.url=jdbc:mysql://localhost:3306/shiro
    dataSource.username=root
    #dataSource.password=
    jdbcRealm.dataSource=$dataSource
    securityManager.realms=$jdbcRealm 
    

      

    1、变量名=  全限定类名会自动创建一个类实例

    2、变量名.属性=值  自动调用相应的setter方法进行赋值

    3、$变量名  引用之前的一个对象实例 

    Authenticator及AuthenticationStrategy

    Authenticator的职责是验证用户帐号,是Shiro API中身份验证核心的入口点: 

    public AuthenticationInfo authenticate(AuthenticationToken authenticationToken)
                throws AuthenticationException; 
    

      

    如果验证成功,将返回AuthenticationInfo验证信息;此信息中包含了身份及凭证;如果验证失败将抛出相应的AuthenticationException实现。

    SecurityManager接口继承了Authenticator,另外还有一个ModularRealmAuthenticator实现,其委托给多个Realm进行验证,验证规则通过AuthenticationStrategy接口指定,默认提供的实现:

    FirstSuccessfulStrategy:只要有一个Realm验证成功即可,只返回第一个Realm身份验证成功的认证信息,其他的忽略;

    AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,和FirstSuccessfulStrategy不同,返回所有Realm身份验证成功的认证信息;

    AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有Realm身份验证成功的认证信息,如果有一个失败就失败了。

    ModularRealmAuthenticator默认使用AtLeastOneSuccessfulStrategy策略。

    假设我们有三个realm:

    myRealm1: 用户名/密码为zhang/123时成功,且返回身份/凭据为zhang/123;

    myRealm2: 用户名/密码为wang/123时成功,且返回身份/凭据为wang/123;

    myRealm3: 用户名/密码为zhang/123时成功,且返回身份/凭据为zhang@163.com/123,和myRealm1不同的是返回时的身份变了;

    1、ini配置文件(shiro-authenticator-all-success.ini) 

    #指定securityManager的authenticator实现
    authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator
    securityManager.authenticator=$authenticator
    
    #指定securityManager.authenticator的authenticationStrategy
    allSuccessfulStrategy=org.apache.shiro.authc.pam.AllSuccessfulStrategy
    securityManager.authenticator.authenticationStrategy=$allSuccessfulStrategy
    

      

    myRealm1=com.github.zhangkaitao.shiro.chapter2.realm.MyRealm1
    myRealm2=com.github.zhangkaitao.shiro.chapter2.realm.MyRealm2
    myRealm3=com.github.zhangkaitao.shiro.chapter2.realm.MyRealm3
    securityManager.realms=$myRealm1,$myRealm3
    

      

    2、测试代码(com.github.zhangkaitao.shiro.chapter2.AuthenticatorTest)

    2.1、首先通用化登录逻辑 

        private void login(String configFile) {
            //1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
            Factory<org.apache.shiro.mgt.SecurityManager> factory =
                    new IniSecurityManagerFactory(configFile);
    
            //2、得到SecurityManager实例 并绑定给SecurityUtils
            org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
            SecurityUtils.setSecurityManager(securityManager);
    
            //3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
    
            subject.login(token);
        }
    

    2.2、测试AllSuccessfulStrategy成功:      

        @Test
        public void testAllSuccessfulStrategyWithSuccess() {
            login("classpath:shiro-authenticator-all-success.ini");
            Subject subject = SecurityUtils.getSubject();
    
            //得到一个身份集合,其包含了Realm验证成功的身份信息
            PrincipalCollection principalCollection = subject.getPrincipals();
            Assert.assertEquals(2, principalCollection.asList().size());
        } 
    

      即PrincipalCollection包含了zhang和zhang@163.com身份信息。

     2.3、测试AllSuccessfulStrategy失败:

        @Test(expected = UnknownAccountException.class)
        public void testAllSuccessfulStrategyWithFail() {
            login("classpath:shiro-authenticator-all-fail.ini");
            Subject subject = SecurityUtils.getSubject();
    } 

    shiro-authenticator-all-fail.ini与shiro-authenticator-all-success.ini不同的配置是使用了securityManager.realms=$myRealm1,$myRealm2;即myRealm验证失败。

    自定义AuthenticationStrategy实现,首先看其API:

    //在所有Realm验证之前调用
    AuthenticationInfo beforeAllAttempts(
    Collection<? extends Realm> realms, AuthenticationToken token) 
    throws AuthenticationException;
    //在每个Realm之前调用
    AuthenticationInfo beforeAttempt(
    Realm realm, AuthenticationToken token, AuthenticationInfo aggregate) 
    throws AuthenticationException;
    //在每个Realm之后调用
    AuthenticationInfo afterAttempt(
    Realm realm, AuthenticationToken token, 
    AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t)
    throws AuthenticationException;
    //在所有Realm之后调用
    AuthenticationInfo afterAllAttempts(
    AuthenticationToken token, AuthenticationInfo aggregate) 
    throws AuthenticationException; 
    

      因为每个AuthenticationStrategy实例都是无状态的,所有每次都通过接口将相应的认证信息传入下一次流程;通过如上接口可以进行如合并/返回第一个验证成功的认证信息。

  • 相关阅读:
    POJ 2251 Dungeon Master
    HDU 3085 Nightmare Ⅱ
    CodeForces 1060 B Maximum Sum of Digits
    HDU 1166 敌兵布阵(树状数组)
    HDOJ 2050 折线分割平面
    HDU 5879 Cure
    HDU 1878 欧拉回路
    HDU 6225 Little Boxes
    ZOJ 2971 Give Me the Number
    HDU 2680 Choose the best route
  • 原文地址:https://www.cnblogs.com/skyLogin/p/6667319.html
Copyright © 2011-2022 走看看