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