Shiro笔记(2)——身份验证

认证(Authentication)其实是一个身份认证的过程,也就是饱受吐槽的“证明你自己是你自己”。用户需要向Shiro提交principals以及credentials以验证它们是否是应用所需。
Principals:事实上是主体的标识,它可以是任何东西,身份证、手机号、邮箱等等。
Credentials:通常是只有主体知道的秘密值,常见的例如密码、指纹、生物虹膜信息等。
在此我们需要引入一些Maven依赖:

    <dependency>  
        <groupId>org.apache.shirogroupId>  
        <artifactId>shiro-coreartifactId>  
        <version>1.4.0version>  
    dependency>  

喜欢什么版本自己定,这不重要。


认证的过程可以笼统地概括为3步:
1. 获取主体的PrincipalsCredentials
2. 提交上述信息;
3. 认证成功则授予相应权限,否则重试或者阻塞。
逐步仔细分析:

1. Collect the Subject’s principals and credentials

UsernamePasswordToken token=new UsernamePasswordToken(username,password);
token.setRememberMe(true);

这个例子中我们使用了UsernamePasswordToken ,这是最常见的用户名/密码令牌,它是 org.apache.shiro.authc.AuthenticationToken的一个接口实现。这里Shiro是不关心用户信息从何而来,无论是HTML页面或者SwingUI界面。采集信息和Shiro是解耦的。

2. Submit the principals and credentials

Subject user=SecurityUtils.getSubject();
user.login(token);

第一行表示获取当前操作的主体,第二行表示使用令牌进行登录操作。

3. Handling Success or Failure

try{
    user.login(token);
}catch(UnknownAccountException uae){
    ...
}catch(IncorrectCredentialsException ice){
    ...
}catch(LockedAccountExcpetion lae){
    ...
}catch(ExcessiveAttemptsException eae){
    ...
}catch(AuthenticationException ae){
    ... 
}

如果没有异常,那么user应该被认证,这时,调用user的isAuthenticated()方法,应当返回true。

将以上三步整合。

//1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager  
    Factory.apache.shiro.mgt.SecurityManager> factory =  
            new IniSecurityManagerFactory("classpath:shiro.ini");  
    //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");  
    try {  
        //4、登录,即身份验证  
        subject.login(token);  
    } catch (AuthenticationException e) {  
        //5、身份验证失败  
    }  

    Assert.assertEquals(true, subject.isAuthenticated()); //断言用户已经登录  

    //6、退出  
    subject.logout();  

验证流程

Shiro笔记(2)——身份验证_第1张图片
1. 应用调用user的login()方法;
2. Subject作为一个委托主体委托给应用的Security Manager,本质上使交由securityManager.login(token),从这里开始验证过程才算真正开始;
3. Authenticator是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实现;
4. Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证,默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多Realm身份验证;
5. Authenticator会把相应的token传入Realm,从Realm获取身份验证信息,如果没有返回/抛出异常表示身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。

Realm

Realm:域,Shiro从Realm获取安全数据,这意味着,Security Manager要验证用户身份时,需要从中获得用户,Realm可以作为安全数据源。
realm是一个接口,内部方法如下:

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

Shiro提供了一些默认实现,当然它也可以自定义实现,我们可以随手码一个:

public MyRealm implements Realm{
    @Override
    public boolean support(AuthenticationToken token){
        return token instanceOf UsernamePasswordToken;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken user= (UsernamePasswordToken) authenticationToken;

        String userLogin=user.getUsername();
        char[] password=user.getPassword();

        User loginResult=null;
        try {
        //从service层获取一个entity
            loginResult=userService.login(userLogin,new String(password));
        }catch (EnterInfoErrorException | NotFoundException e){
            e.printStackTrace();
            throw new AuthenticationException(e.getMessage());
        }
        return new SimpleAuthenticationInfo(loginResult,user.getPassword(),this.getName());
    }
}

Shiro默认实现的Realm
Shiro笔记(2)——身份验证_第2张图片
一般来说,只需要继承AuthorizingRealm即可,如图所示,它的父类是AuthenticationRealm,祖父类是CachingRealm,后者带有缓存实现,必须指出,它们都是抽象类。以下是Realm的默认实现:

  • org.apache.shiro.realm.text.IniRealm:[users]部分指定用户名/密码及其角色;[roles]部分指定角色即权限信息;
  • org.apache.shiro.realm.text.PropertiesRealm:user.username=password,role1,role2指定用户名/密码,role.role1=permission1,perimission2指定角色及权限信息;
  • 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;

Authenticator及AuthenticationStrategy

//它是一个接口
package org.apache.shiro.authc;

public interface Authenticator {
    AuthenticationInfo authenticate(AuthenticationToken var1) throws AuthenticationException;
}

如果验证成功,将返回AuthenticationInfo验证信息;此信息中包含了身份及凭证;如果验证失败将抛出相应的AuthenticationException实现。
SecurityManager接口继承了Authenticator,另外还有一个ModularRealmAuthenticator实现,其委托给多个Realm进行验证,验证规则通过AuthenticationStrategy接口指定,默认提供的实现:

  • FirstSuccessfulStrategy:只要有一个Realm验证成功即可,只返回第一个Realm身份验证成功的认证信息,其他的忽略;
  • AtLeastOneSuccessfulStrategy:只要有一个Realm验证成功即可,和FirstSuccessfulStrategy不同,返回所有Realm身份验证成功的认证信息;
  • AllSuccessfulStrategy:所有Realm验证成功才算成功,且返回所有Realm身份验证成功的认证信息,如果有一个失败就失败了。

ModularRealmAuthenticator默认使用AtLeastOneSuccessfulStrategy策略。

你可能感兴趣的:(Shiro笔记(2)——身份验证)