关于身份认证中的Authenticator及AuthenticationStrategy


在流程图3中,有Authenticator和AuthenticationStrategy2个接口。

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

        它只有一个方法:

public AuthenticationInfo authenticate(AuthenticationToken authenticationToken)
            throws AuthenticationException;

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

选中AuthenticationInfo,按住Ctrl+t,可以看到它的继承体系。

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不同的是返回时的身份变了;


三个reaml文件如下:

package com.lgy.shiro;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.Realm;

public class MyRealm1 implements Realm {
    public String getName() {
        return "myrealm1";
    }

    public boolean supports(AuthenticationToken token) {
        return token instanceof UsernamePasswordToken; //仅支持UsernamePasswordToken类型的Token
    }

    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());
    }
}


package com.lgy.shiro;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.Realm;

public class MyRealm2 implements Realm {
    public String getName() {
        return "myrealm2";
    }

    public boolean supports(AuthenticationToken token) {
        return token instanceof UsernamePasswordToken; //仅支持UsernamePasswordToken类型的Token
    }

    public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String)token.getPrincipal();  //得到用户名
        String password = new String((char[])token.getCredentials()); //得到密码
        if(!"wang".equals(username)) {
            throw new UnknownAccountException(); //如果用户名错误
        }
        if(!"123".equals(password)) {
            throw new IncorrectCredentialsException(); //如果密码错误
        }
        //如果身份认证验证成功,返回一个AuthenticationInfo实现;
        return new SimpleAuthenticationInfo(username, password, getName());
    }
}


package com.lgy.shiro;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.Realm;

public class MyRealm3 implements Realm {
    public String getName() {
        return "myrealm3";
    }

    public boolean supports(AuthenticationToken token) {
        return token instanceof UsernamePasswordToken; //仅支持UsernamePasswordToken类型的Token
    }

    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 + "163@qq.com", password, getName());
    }
}


封装的login代码如下:

    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);
    }

    @After
    public void tearDown() throws Exception {
        ThreadContext.unbindSubject();//退出时请解除绑定Subject到线程 否则对下次测试造成影响
    }


上面说的:

默认策越(ModularRealmAuthenticator默认使用AtLeastOneSuccessfulStrategy策略。)DEMO

ini文件:

[main]

#声明一个realm

myRealm1=com.lgy.shiro.MyRealm1
myRealm2=com.lgy.shiro.MyRealm2
myRealm3=com.lgy.shiro.MyRealm3
#指定securityManager的realms实现

securityManager.realms=$myRealm1,$myRealm2,$myRealm3

测试如下:

 @Test
    public void testStrategyWithdefault() {
    	
    			
        login("classpath:shiro-auth-default.ini");
//        login("classpath:shiro-realm.ini");
        Subject subject = SecurityUtils.getSubject();
        /*
        boolean b = subject.isAuthenticated();
        System.out.println(b);*/
        //得到一个身份集合,其包含了Realm验证成功的身份信息
        PrincipalCollection principalCollection = subject.getPrincipals();
        System.out.println(principalCollection.asList().size());   //返回所有的身份凭证信息
        //Assert.assertEquals(2, principalCollection.asList().size());
   }

      验证信息是zhang/123, myRealm1和myRealm2是能通过的,打印返回的size为2,正如前面所说的:只要有一个Realm验证成功即可,和FirstSuccessfulStrategy不同,返回所有Realm身份验证成功的认证信息;



FirstSuccessfulStrategy策越,demo如下:

ini文件:

[main]

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

#声明一个realm

myRealm1=com.lgy.shiro.MyRealm1
myRealm2=com.lgy.shiro.MyRealm2
myRealm3=com.lgy.shiro.MyRealm3
#指定securityManager的realms实现

securityManager.realms=$myRealm1,$myRealm2,$myRealm3

测试代码:

@Test
    public void testStrategyWithdefirst() { 
    	login("classpath:shiro-auth-first.ini");
    	Subject subject = SecurityUtils.getSubject();
    	System.out.println(subject.isAuthenticated());
    	
    	PrincipalCollection principalCollection = subject.getPrincipals();
        System.out.println(principalCollection.asList().size());
    }
      验证信息是zhang/123, myRealm1和myRealm2是能通过的,打印返回的size为1,正如前面所说的只要有一个Realm验证成功即可,只返回第一个Realm身份验证成功的认证信息,其他的忽略;


AllSuccessfulStrategy测试,demo如下:

ini文件:

[main]

#指定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 

#声明一个realm

myRealm1=com.lgy.shiro.MyRealm1
myRealm2=com.lgy.shiro.MyRealm2
myRealm3=com.lgy.shiro.MyRealm3
#指定securityManager的realms实现

securityManager.realms=$myRealm1,$myRealm2,$myRealm3


测试文件:

    @Test
    public void testStrategyWithdeAll() { 
    	login("classpath:shiro-auth-all.ini");
    	Subject subject = SecurityUtils.getSubject();
    	System.out.println(subject.isAuthenticated());
    	
    	PrincipalCollection principalCollection = subject.getPrincipals();
        System.out.println(principalCollection.asList().size());
    }

       验证信息是zhang/123, myRealm1和myRealm3是能通过的,但是myRealm2不是通过,所以会抛出异常,测试部通过。 将myRealm2中的wang改为zhang,所以验证能通过,打印为2,为什么?因为返回凭证中myRealm1和myRealm2一样被当做同一个凭证。











  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值