shiro之身份認證

1、用戶須要提供principals (身份)和credentials(證實)給shiro,從而應用能驗證用戶身份。java

principals:身份,即主體的標識屬性,能夠是任何東西,如用戶名、郵箱等,惟一便可。一個主體能夠有多個principals,但只有一個Primary principals,通常是用戶名/密碼/手機號。
credentials:證實/憑證,即只有主體知道的安全值,如密碼/數字證書等。mysql

最多見的principals和credentials組合就是用戶名/密碼。web

測試代碼:sql

/**  
 * @Project: testshiro
 * @Title: TestShiro.java
 * @Package com.yuan.shiro.test
 * @author yuan
 * @date 2016年7月4日 下午1:28:28
 * @Copyright: 2016
 * @version V1.0  
*/

package com.yuan.shiro.test;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Test;

/**
 * @ClassName TestShiro
 * @author yuan
 * @version 1.0
 */

public class TestShiro {

	@Test
	public void testLogin(){
		//1.獲取SecurityManager工廠,此處使用ini配置文件初始化SecurityManager工廠
		Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:com/yuan/shiro/test/shiro.ini");
		//2.獲得SecurityManager實例,並綁定到SecurityUtils
		SecurityManager securityManager = factory.getInstance();
		SecurityUtils.setSecurityManager(securityManager);
		//3.經過SecurityUtils獲得Subject獲得Subject,其會自動綁定到當前線程,若是在web環境在請求結束時須要解除綁定
		//而後獲取身份驗證的Token,如用戶名/密碼;
		Subject subject = SecurityUtils.getSubject();
		UsernamePasswordToken token = new UsernamePasswordToken("u1","1234");
		try {
			//4.登陸進行身份驗證,其會自動委託給SecurityManager.login方法進行登陸
			//此處與ini中配置文件進行比對
			subject.login(token);
			System.out.println("登陸成功");
		} catch (AuthenticationException e) {
			//5.驗證失敗
			//常見的如:DisabledAccountException(禁用的賬號)、LockedAccountException(鎖定的賬號)、UnknownAccountException(錯誤的賬號)
			//ExcessiveAttemptsException(登陸失敗次數過多)、IncorrectCredentialsException (錯誤的憑證)、ExpiredCredentialsException(過時的憑證)
			System.out.println("登陸失敗");
			e.printStackTrace();
		}
		//判斷用戶是否已登陸
		if( subject.isAuthenticated() ){
			System.out.println("已登陸");
		}else{
			System.out.println("未登陸");
		}
		
		//6.退出,自動委託給SecurityManager.logout方法退出。
		subject.logout();
	}
}

shiro.ini數據庫

[users]
u1=123
u2=123

2、身份驗證的步驟:apache

一、收集用戶身份/憑證,即如用戶名/密碼;
二、調用Subject.login 進行登陸,若是失敗將獲得相應的AuthenticationException 異常,根據異常提示用戶錯誤信息;不然登陸成功;api

3、身份認證的流程:緩存

一、首先調用Subject.login(token)進行登陸,其會自動委託給Security Manager,調用以前必須經過SecurityUtils. setSecurityManager()設置;
二、SecurityManager負責真正的身份驗證邏輯;它會委託給Authenticator進行身份驗證;
三、Authenticator纔是真正的身份驗證者,Shiro API中核心的身份認證入口點,此處能夠自定義插入本身的實現;
四、Authenticator可能會委託給相應的AuthenticationStrategy進行多Realm身份驗證,默認ModularRealmAuthenticator會調用AuthenticationStrategy進行多Realm身份驗證;
五、Authenticator 會把相應的token 傳入Realm,從Realm 獲取身份驗證信息,若是沒有返回/拋出異常表示身份驗證失敗了。此處能夠配置多個Realm,將按照相應的順序及策略進
行訪問。安全

4、realmide

Realm:域,Shiro 從從Realm獲取安全數據(如用戶、角色、權限),就是說SecurityManager要驗證用戶身份,那麼它須要從Realm獲取相應的用戶進行比較以肯定用戶身份是否合法;也須要從Realm獲得用戶相應的角色/權限進行驗證用戶是否能進行操做;能夠把Realm當作DataSource , 即安全數據源。

org.apache.shiro.realm.Realm接口有三個方法

String getName(); //返回一個惟一的Realm名字
boolean supports(AuthenticationToken token); //判斷此Realm是否支持此Token
AuthenticationInfo getAuthenticationInfo(AuthenticationToken token)
             throws AuthenticationException; //根據Token獲取認證信息

a、單Realm配置

一、自定義Realm實現

/**  
 * @Project: testshiro
 * @Title: TestRealm1.java
 * @Package com.yuan.shiro.realm
 * @author yuan
 * @date 2016年7月4日 下午3:14:41
 * @Copyright: 2016
 * @version V1.0  
*/

package com.yuan.shiro.realm;

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;

/**
 * @ClassName TestRealm1
 * @author yuan
 * @version 1.0
 */

public class TestRealm1 implements Realm{

	/**
	 * <p>Title: getAuthenticationInfo</p> 
	 * <p>Description: 根據Token獲取認證信息</p> 
	 * @param token
	 * @return
	 * @throws AuthenticationException 
	 * @see org.apache.shiro.realm.Realm#getAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken) 
	*/
	
	@Override
	public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token)
			throws AuthenticationException {
		String userName = (String)token.getPrincipal(); //獲得用戶名
		String pwd = new String((char[])token.getCredentials()); //獲得密碼
		if(!"u1".equals(userName)) {
		throw new UnknownAccountException(); //若是用戶名錯誤
		}
		if(!"123".equals(pwd)) {
		throw new IncorrectCredentialsException(); //若是密碼錯誤
		}
		//若是身份認證驗證成功,返回一個AuthenticationInfo實現;
		return new SimpleAuthenticationInfo(userName, pwd, this.getName());
	}

	/** 
	 * <p>Title: getName</p> 
	 * <p>Description:返回一個惟一的Realm名字 </p> 
	 * @return 
	 * @see org.apache.shiro.realm.Realm#getName() 
	*/
	
	@Override
	public String getName() {
		return "TestRealm1";
	}

	/**
	 * <p>Title: supports</p> 
	 * <p>Description: 判斷此Realm是否支持此Token</p> 
	 * @param token
	 * @return 
	 * @see org.apache.shiro.realm.Realm#supports(org.apache.shiro.authc.AuthenticationToken) 
	*/
	
	@Override
	public boolean supports(AuthenticationToken token) {
		//僅支持UsernamePasswordToken 類型的Token
		return token instanceof UsernamePasswordToken;
	}

	
}

二、ini配置文件指定自定義Realm實現

[main]
#聲明一個realm
testRealm1=com.yuan.shiro.realm.TestRealm1
#指定securityManager的realms實現
securityManager.realms=$testRealm1

經過$name 來引入自定義的realm

b、多Realm配置

[main]
#聲明一個realm
testRealm1=com.yuan.shiro.realm.TestRealm1
testRealm2=com.yuan.shiro.realm.TestRealm2
#指定securityManager的realms實現
securityManager.realms=$testRealm1,$testRealm2

securityManager會按照realms指定的順序進行身份認證,若是刪除「securityManager.realms=$myRealm1,$myRealm2」,那麼securityManager會按照realm聲明的順序進行使用。

c、shiro默認的Realm

通常繼承AuthorizingRealm(受權)便可;其繼承了AuthenticatingRealm(即身份驗證),也間接繼承了CachingRealm(帶有緩存實現)。其中主要默認實現以下:
org.apache.shiro.realm.text.IniRealm:[users]部分指定用戶名/密碼及其角色;[roles]部分指定角色即權限信息;
org.apache.shiro.realm.text.PropertiesRealm:user.username=password,role1,role2指定用戶名/密碼及其角色;role.role1=permission1,permission2指定角色及權限信息;
org.apache.shiro.realm.jdbc.JdbcRealm:經過sql查詢相應的信息,如「select password fromusers where username = ?」獲取用戶密碼,「select password, password_salt from users whereusername = ?」獲取用戶密碼及鹽;「select role_name from user_roles where username = ?」獲取用戶角色;「select permission from roles_permissions where role_name = ?」獲取角色對應的權限信息;也能夠調用相應的api進行自定義sql;

d、jdbcRealm

一、數據庫下建表,shiro默認表

create table users (
  id bigint auto_increment,
  username varchar(100),
  password varchar(100),
  password_salt varchar(100),
  constraint pk_users primary key(id)
) charset=utf8 ENGINE=InnoDB;
create unique index idx_users_username on users(username);

create table user_roles(
  id bigint auto_increment,
  username varchar(100),
  role_name varchar(100),
  constraint pk_user_roles primary key(id)
) charset=utf8 ENGINE=InnoDB;
create unique index idx_user_roles on user_roles(username, role_name);

create table roles_permissions(
  id bigint auto_increment,
  role_name varchar(100),
  permission varchar(100),
  constraint pk_roles_permissions primary key(id)
) charset=utf8 ENGINE=InnoDB;
create unique index idx_roles_permissions on roles_permissions(role_name, permission);

insert into users(username,password)values('u1','123');

二、ini配置

[main]
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
dataSource=org.apache.commons.dbcp.BasicDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/online
dataSource.username=root
dataSource.password=admin
jdbcRealm.dataSource=$dataSource
securityManager.realms=$jdbcRealm

變量名=全限定類名會自動建立一個類實例
變量名.屬性=值自動調用相應的setter方法進行賦值
$變量名引用以前的一個對象實例

三、測試

5、Authenticator

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:
testRealm1: 用戶名/密碼爲u1/123時成功,且返回身份/憑據爲u1/123;
testRealm2: 用戶名/密碼爲u1/123 時成功,且返回身份/憑據爲u1@1.com/123,
和testRealm1不一樣的是返回時的身份變了;

一、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

testRealm1=com.yuan.shiro.realm.TestRealm1
testRealm2=com.yuan.shiro.realm.TestRealm2
securityManager.realms=$testRealm1,$testRealm2

測試:

/**  
 * @Project: testshiro
 * @Title: TestAuthenticator.java
 * @Package com.yuan.shiro.test
 * @author yuan
 * @date 2016年7月4日 下午4:01:30
 * @Copyright: 2016
 * @version V1.0  
 */

package com.yuan.shiro.test;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Test;

/**
 * @ClassName TestAuthenticator
 * @author yuan
 * @version 1.0
 */

public class TestAuthenticator {

	@Test
	public void testAllSuccessfulStrategyWithSuccess() {
		login("classpath:com/yuan/shiro/test/shiro.ini");
		Subject subject = SecurityUtils.getSubject();

		// 獲得一個身份集合,其包含了Realm驗證成功的身份信息
		PrincipalCollection principalCollection = subject.getPrincipals();
		System.out.println("個數====>"+principalCollection.asList().size());
	}

	/**
	 * 登陸
	 * 
	 * @MethodName login
	 * @author yuan
	 * @date 2016年7月4日 下午4:02:08
	 * @return void
	 */
	private void login(String config) {
		// 一、獲取SecurityManager工廠,此處使用Ini配置文件初始化SecurityManager
		Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory(
				config);

		// 二、獲得SecurityManager實例 並綁定給SecurityUtils
		org.apache.shiro.mgt.SecurityManager securityManager = factory
				.getInstance();
		SecurityUtils.setSecurityManager(securityManager);

		// 三、獲得Subject及建立用戶名/密碼身份驗證Token(即用戶身份/憑證)
		Subject subject = SecurityUtils.getSubject();
		UsernamePasswordToken token = new UsernamePasswordToken("u1", "123");

		try {
			subject.login(token);
		} catch (AuthenticationException e) {
			System.out.println("登陸失敗");
			e.printStackTrace();
		}
	}
}

相關文章
相關標籤/搜索