Shiro-多Realm验证

2018-01-09 10:19:32来源:oschina作者:为了美好的明天人点击

分享

1.多Realm验证


  存在这样一种场景,同一个密码可能在MqSQL中存储,也可能在Oracle中存储,有可能MqSQL中使用的是MD5加密算法,而Oracle使用SHA1加密算法。这就需要有多个Realm以及认证策略的问题。


  


  通过查看源码可以看到ModularRealmAuthenticator.class 中的doAuthenticate


protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
assertRealmsConfigured();
Collection realms = getRealms();
if (realms.size() == 1) {
return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);
} else {
return doMultiRealmAuthentication(realms, authenticationToken);
}
}

即可以看到如果有一个Realm 使用的是doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);


  如果有多个Realm 使用的是doMultiRealmAuthentication(realms, authenticationToken);


  所以我们可以配置多个Realm 给到ModularRealmAuthenticator 这个bean,将ModularRealmAuthenticator 单独配置为一个bean,将这个bean 配置给SecurityManager


 1).添加第二个Realm SecondRealm.java


package com.java.shiro.realms;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.util.ByteSource;
public class SecondRealm extends AuthenticatingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
System.out.println("[SecondRealm] doGetAuthenticationInfo " + token);
// 1. 把AuthenticationToken 转换为UsernamePasswordToken
UsernamePasswordToken up = (UsernamePasswordToken) token;
// 2. 从UsernamePasswordToken 中来获取username
String username = up.getUsername();
// 3. 调用数据库的方法,从数据库中查询username对应的用户记录
System.out.println("从数据库中获取userName :" + username + " 所对应的用户信息.");
// 4. 若用户不存在,则可以抛出 UnknownAccoountException 异常
if ("unknown".equals(username)) {
throw new UnknownAccountException("用户不存在");
}
// 5. 根据用户信息的情况,决定是否需要抛出其他的AuthencationException 异常 假设用户被锁定
if ("monster".equals(username)) {
throw new LockedAccountException("用户被锁定");
}
// 6. 根据用户的情况,来构建AuthenticationInfo 对象并返回,通常使用的是
// SimpleAuthenticationInfo
// 以下信息是从数据库获取的.
Object principal = username; // principal 认证的实体信息.
// 可以是username,也可以是数据表对应的用户的实体类对象
// String credentials = "fc1709d0a95a6be30bc5926fdb7f22f4"; // credentials:密码
String credentials = null; // credentials:密码
String realmName = getName();
AuthenticationInfo info = null;/*new SimpleAuthenticationInfo(principal, credentials, realmName);*/

if("admin".equals(username)){
credentials = "ce2f6417c7e1d32c1d81a797ee0b499f87c5de06";
}else if("user".equals(username)){
credentials = "073d4c3ae812935f23cb3f2a71943f49e082a718";
}

ByteSource credentialsSalt = ByteSource.Util.bytes(username);//这里的参数要给个唯一的;

info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
return info;
}public static void main(String[] args) {
String hashAlgorithmName = "SHA1";
String credentials = "123456";
int hashIterations = 1024;
ByteSource credentialsSalt = ByteSource.Util.bytes("admin");
Object obj = new SimpleHash(hashAlgorithmName, credentials, credentialsSalt, hashIterations);
System.out.println(obj);
}
}

  


2). 修改 applicationContext.xml的配置文件


<?xml version="1.0" encoding="UTF-8"?>
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
























































depends-on="lifecycleBeanPostProcessor"/>

















/login.jsp= anon
/shiro/login= anon
/shiro/logout = logout
# everything else requires authentication:/** = authc



3).在UsernamePasswordToken.class 的


public char[] getPassword() {
return password;
}

  处打断点,会看到断点停两次。


  由于两种都使用的HashedCredentialsMatcher 时的两种算法:



测试成功:


[FirstRealm] doGetAuthenticationInfo org.apache.shiro.authc.UsernamePasswordToken - admin, rememberMe=true 从数据库中获取userName :admin 所对应的用户信息. [SecondRealm] doGetAuthenticationInfo org.apache.shiro.authc.UsernamePasswordToken - admin, rememberMe=true 从数据库中获取userName :admin 所对应的用户信息.


  这两个使用顺序的,因为我们在配置文件中的配置,










2. Shiro 认证策略


  1).如果有多个Realm,怎样才算是认证成功,这就需要认证策略。


  认证策略主要使用的是AuthenticationStrategy接口


   这个接口由三个实现类:



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

  通过debug 可以看出,默认使用的是AtLeastOneSuccessfulStrategy



  


  为了便于观看,修改SecondRealm中的


    info = new SimpleAuthenticationInfo("seconde", credentials, credentialsSalt, realmName);


  2). 如何切换认证策略


    切换成AllSuccessfulStrategy 即所有认证策略都通过了,才算认证成功。


    可以看出 认证策略是ModularRealmAuthenticator 类的一个属性 authenticationStrategy


    即在applicationContext.xml中添加配置:












修改其中一个Realm的密码 为一个错误的密码:


    if("admin".equals(username)){
credentials = "ce2f6417c7e1d32c1d81a797ee0b499f87c5de06---";
}else if("user".equals(username)){
credentials = "073d4c3ae812935f23cb3f2a71943f49e082a718---";
}

则可以看到修改后的验证策略为:org.apache.shiro.authc.pam.AllSuccessfulStrategy@72f51247



[FirstRealm] doGetAuthenticationInfo org.apache.shiro.authc.UsernamePasswordToken - admin, rememberMe=true 从数据库中获取userName :admin 所对应的用户信息. [SecondRealm] doGetAuthenticationInfo org.apache.shiro.authc.UsernamePasswordToken - admin, rememberMe=true 从数据库中获取userName :admin 所对应的用户信息. 登录失败:Unable to acquire account data from realm [com.java.shiro.realms.SecondRealm@349102eb]. The [org.apache.shiro.authc.pam.AllSuccessfulStrategy implementation requires all configured realm(s) to operate successfully for a successful authentication.

最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台