JDBC Realm 使用 

1、数据库及依赖 

<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>
		5.1.25
	</version>
</dependency>
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid</artifactId>
	<version>
		0.2.23
	</version>
</dependency>

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

2、到数据库 shiro 下建三张表:users(用户名/密码)、user_roles(用户/角色)、roles_permissions (角色/权限),具体请参照 shiro-example-chapter2/sql/shiro.sql;并添加一个用户记录,用 户名/密码为 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、$变量名 引用之前的一个对象实例  

4、测试代码请参照 com.github.zhangkaitao.shiro.chapter2.LoginLogoutTest 的 testJDBCRealm 方法,和之前的没什么区别。

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 验证失败。  

对 于 AtLeastOneSuccessfulStrategy 和 FirstSuccessfulStrategy 的区别, 请参照 testAtLeastOneSuccessfulStrategyWithSuccess和testFirstOneSuccessfulStrategyWithSuccess测试方法。唯一不同点一个是返回所有验证成功的 Realm 的认证信息;另一个是只返回第一个验证成功的 Realm 的认证信息。  

自定义 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 实例都是无状态的,所有每次都通过接口将相应的认证信 息传入下一次流程;通过如上接口可以进行如合并/返回第一个验证成功的认证信息。  

自定义实现时一般继承 org.apache.shiro.authc.pam.AbstractAuthenticationStrategy 即可,具体 可以参考代码 com.github.zhangkaitao.shiro.chapter2.authenticator.strategy 包 下 OnlyOneAuthenticatorStrategy 和 AtLeastTwoAuthenticatorStrategy。  

到此基本的身份验证就搞定了,对于 AuthenticationToken 、AuthenticationInfo 和 Realm 的 详细使用后续章节再陆续介绍


关注极客云图了解更多内容