Java環境下shiro的測試-認證與授權
Java環境下shiro的測試
1.匯入依賴的核心jar包
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
2.認證程式
2.1 構建users配置檔案 xxx.ini doGetAuthenticationInfo方法從該配置檔案中獲取資料與token中比對
[users] test=123456 lisi=123456
測試程式
public class TestShiro { public static void main(String[] args) { //獲取安全管理器工廠 IniSecurityManagerFactory iniSecurityManagerFactory = new IniSecurityManagerFactory("classpath:shiro.ini"); //獲取安全管理器 SecurityManager securityManager = iniSecurityManagerFactory.getInstance(); //set認證器 SecurityUtils.setSecurityManager(securityManager); //subject發起認證,獲取subject Subject subject = SecurityUtils.getSubject(); AuthenticationToken authenticationToken = new UsernamePasswordToken("test","123456"); //認證失敗會丟擲異常 密碼:CredentialsException 賬戶:UnknownAccountException try { subject.login(authenticationToken); } catch (AuthenticationException e) { e.printStackTrace(); } //當前subject是否認證通過 boolean authenticated = subject.isAuthenticated(); System.out.println(authenticated); } }
認證流程:
token攜帶身份和憑證資訊--->subject發起認證--->SimpleAccountRealm(doGetAuthenticationInfo)獲取配置檔案中的使用者資訊---->CredentialsMatcher介面的實現類SimpleCredentialsMatcher:doCredentialsMatch方法對配置檔案中的資訊與token攜帶的資訊進行比對--->認證成功或者失敗。
3.Shiro框架中的關鍵物件:
AuthenticatingRealm //抽象類 //3.關鍵屬性 該屬性為憑證匹配器 CredentialsMatcher credentialsMatcher; //1.該方法為抽象方法 其作用使用來獲取資料 protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken var1) throws AuthenticationException;
SimpleAccountRealm
//2.實現了AuthenticatingRealm抽象方法,用來獲取配置檔案中的使用者資訊,該類不做資料比對
SimpleCredentialsMatcher
//4.shiro中預設的憑證匹配器
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
Object tokenCredentials = this.getCredentials(token);
Object accountCredentials = this.getCredentials(info);
return this.equals(tokenCredentials, accountCredentials);
}
必須要知道的類與介面,不然很難理解自定義Realm時屬性為什麼設定,使用哪種實現類方法等等:
以下程式碼或者截圖貼出最重要的地方.
類繼承關係
AuthenticatingRealm類
abstract class AuthenticatingRealm{
//憑證匹配器 介面,其實現類做資料比對
private CredentialsMatcher credentialsMatcher;
//獲取配置檔案中的使用者資訊
protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken var1) throws AuthenticationException;
}
該抽象方法返回型別AuthenticationInfo介面:
AuthorizingRealm類 抽象方法後面測試授權時使用
abstract class AuthorizingRealm{
//
//該抽象方法 獲取資料 獲取授權的資料
protected abstract AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection var1);
}
該抽象方法返回型別AuthorizationInfo介面:
CredentialsMatcher憑證匹配器介面:
其中:SimpleCredentialsMatcher是shiro中預設的憑證匹配器,其子類Hashxxx等都是做加密認證時使用
4.開發自定義Realm
public class MyRealm extends AuthenticatingRealm {
//實現抽象方法doGetAuthenticationInfo
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {
String principal =(String) authenticationToken.getPrincipal();
//查庫取回User物件
SqlSession sqlSession = null;
try {
sqlSession = MySqlSession.getSqlSession();
} catch (IOException e) {
e.printStackTrace();
}
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserByUserName(principal);
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(principal,user.getPassword(),this.getName());
return authenticationInfo;
}
}
4.1通知shiro使用自定義realm
[main]
#自定義 realm
customRealm=com.nyist.test.MyRealm
#將realm設定到securityManager
securityManager.realms=$customRealm
注意:需要匯入一個jar
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
5.shiro的加密認證方式
5.1.使用shiro提供的Md5Hash類為一個字串加密進行測試
public class TestMD5 {
public static void main(String[] args) {
Md5Hash hash = new Md5Hash("123456","salt",1024);
String s = hash.toHex();
System.out.println(s);
//a18d2133f593d7b0e3ed488560404083
}
}
5.2.修改配置檔案,加入憑證匹配器的相關配置
[main]
#自定義憑證匹配器
hashedCredentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#憑證匹配器通知AuthenticatingRealm,由於自定義realm繼承了AuthenticatingRealm,直接設定已有的MyRealm屬性即可
#自定義 realm
customRealm=com.nyist.test.MyRealm
customRealm.credentialsMatcher=$hashedCredentialsMatcher
hashedCredentialsMatcher.hashAlgorithmName=MD5
hashedCredentialsMatcher.hashIterations=1024
#將realm設定到securityManager .realms使用set方式賦值
securityManager.realms=$customRealm
坑:Caused by: java.lang.IllegalStateException: Required 'hashAlgorithmName' property has not been set. This is required to execute the hashing algorithm.
HashedCredentialsMatcher類中set方法非常規,set方法為:setHashAlgorithmName
為什麼這麼設定憑證匹配器?
自定義MyRealm extends AuthorizingRealm,實現兩個抽象方法
AuthenticationInfo doGetAuthenticationInfo()來自於AuthenticatingRealm類,獲取認證資料
AuthorizationInfo doGetAuthorizationInfo()來自於AuthorizingRealm類,獲取授權資料
public class MyRealm extends AuthorizingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {
String principal =(String) authenticationToken.getPrincipal();
//userDao.queryUserByUserName
SqlSession sqlSession = null;
try {
sqlSession = MySqlSession.getSqlSession();
} catch (IOException e) {
e.printStackTrace();
}
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserByUserName(principal);
/*
* ByteSource.Util.bytes("salt") 鹽欄位來自資料庫,憑證匹配器不能寫死鹽值
* 安全管理器可以獲取到AuthenticationInfo中的鹽值 對使用者介面的憑證加密
* */
AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(principal,user.getPassword(),ByteSource.Util.bytes("salt"),this.getName());
return authenticationInfo;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//獲取身份資訊 Principal使用者名稱、手機號、郵箱地址等 一個主體可以有多個身份,但是必須有一個主身份(Primary Principal)
String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
/*
* 使用者----角色----許可權
* 中間表 中間表
* */
//由primaryPrincipal查庫--->獲得角色info ---->獲取許可權info
SqlSession sqlSession = null;
try {
sqlSession = MySqlSession.getSqlSession();
} catch (IOException e) {
e.printStackTrace();
}
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserByUserName(primaryPrincipal);
//測試基於角色的授權
/*if (primaryPrincipal.equals(user.getUsername())){
// class SimpleAuthorizationInfo implements AuthorizationInfo
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.addRole("super");
return authorizationInfo;
}*/
//測試基於資源的授權
if(primaryPrincipal.equals(user.getUsername())){
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.addStringPermission("user:delete");
authorizationInfo.addStringPermissions(Arrays.asList("admin:delete","admin:add"));
return authorizationInfo;
}
return null;
}
}
一張圖看懂認證授權關係:[待修改,圖太劣質]
授權的api
基於角色
//判斷當前主體是否包含此角色 boolean b = subject.hasRole("super"); List<String> list = Arrays.asList("super", "admin"); //判斷當前主體是否包含某個角色 boolean[] booleans = subject.hasRoles(list); //判斷當前主體是否包含全部的角色 boolean b = subject.hasAllRoles(list);
基於資源
boolean b = subject.isPermitted("admin:delete"); String[] strs={"admin:delete", "admin:add"}; boolean[] permitted = subject.isPermitted(strs); boolean permittedAll = subject.isPermittedAll(strs);
資源許可權的識別符號
許可權字串的規則是:“資源識別符號:操作:資源例項識別符號”,意思是對哪個資源的哪個例項具有什麼操作,“:”是資源/操作/例項的分割符,許可權字串也可以使用*萬用字元。
例子:
- 使用者建立許可權:user:create,或user:create:*
- 使用者修改例項001的許可權:user:update:001
- 使用者例項001的所有許可權:user:*:001