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

来源:互联网 发布:fcitx无法启动 ubuntu 编辑:程序博客网 时间:2024/06/09 13:25

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

    <dependency>          <groupId>org.apache.shiro</groupId>          <artifactId>shiro-core</artifactId>          <version>1.4.0</version>      </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<org.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();  

验证流程

这里写图片描述
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
这里写图片描述
一般来说,只需要继承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策略。

原创粉丝点击