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(); } } }