shiro框架學習筆記
最近學習了 許可權框架shiro的知識,做一下 學習的筆記
使用ini
這是shiro 最簡單的用法,首先建立一個demo.ini
檔案,裡面寫入如下的內容
[users]
xiezihao=123456,admin
[roles]
admin = user.insert,user.update
下面對上面的配置進行一下解釋
[users] 代表的是使用者,之後在程式碼中做登入的時候使用,比如這裡的配置中,的一是就是 ,使用者名稱是xiezihao,密碼是123456,擁有一個admin的角色。
[roles] 代表的是角色的意思,一個使用者可以對應有多個角色,這裡的一是是,admin這個角色擁有,user.insert 和 user.update 兩種操作的許可權
下面將通過示例程式碼演示,如何利用這個ini檔案實現登入,和許可權的認證
public void t(){
//設定使用的realm
IniRealm iniRealm = new IniRealm("classpath:demo.ini");
//新建一個安全管理器,並將realm設定到這個管理器中去
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm (iniRealm);
//將設定好的 安全管理器放入安全工具類中,然後用 安全工具類來獲取subject
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
//建立登入用的token
UsernamePasswordToken token = new UsernamePasswordToken();
token.setUsername("xiezihao" );
token.setPassword("123456".toCharArray());
//登入
subject.login(token);
if(subject.isAuthenticated()){
System.out.println("登入成功");
}
if(subject.hasRole("admin")){
System.out.println("擁有admin的身份");
}
if(subject.isPermitted("user.insert")){
System.out.println("擁有 user.insert 的許可權");
}
}
後面的例子其實都和這個比較的相似,都是按照如下順序
- 拿到一個realm
- 用這個realm 設定出一個安全管理器
- 為安全工具類設定安全管理器,然後以此創建出subject
- 利用subject做後續的操作
自定義的realm
有的時候框架給出的realm不能滿足我們實際的需求,我們可以自定義realm,新建一個類,讓這個類去繼承 AuthorizingRealm
然後實現裡面的兩個方法:doGetAuthorizationInfo
授權、doGetAuthenticationInfo
登入認證
下面用一個實際的例子 來說明
public class DemoRealm extends AuthorizingRealm {
//授權的時候執行
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("開始鑑權操作");
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
HashSet<String> roles = new HashSet<>();
HashSet<String> permissions = new HashSet<>();
//將使用者的角色和許可權填入set集合 在實際的情況下這一步會從資料庫中獲取
roles.add("admin");
permissions.add("user.insert");
//將使用者的角色集合和許可權集合放入到AuthorizationInfo中去
simpleAuthorizationInfo.setRoles(roles);
simpleAuthorizationInfo.setStringPermissions(permissions);
return simpleAuthorizationInfo;
}
//登入時候執行
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("開始使用者登入操作");
//獲取使用者登入的使用者名稱和密碼
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
String username = usernamePasswordToken.getUsername();
char[] password = usernamePasswordToken.getPassword();
//這裡我們模擬一下許可權認證,實際情況上這裡會從資料庫拿資料
if (username.equals("xiezihao") && new String(password).equals("123456")) {
/**
* 如果認證成功返回一個AuthenticationInfo物件,這三個引數分別是
* 使用者名稱、密碼、和realm名字 realm的名字隨便取好了主要是用來區分的作用
*/
return new SimpleAuthenticationInfo("xiezihao", "123456", "demoRealm");
}
//如果登入失敗就返回null
return null;
}
}
下面我們利用一個測試類來測試一下。測試類的寫法可以參考 ini 這個測試的方法
@Test
public void demoRealmT() {
// 新建一個 我們自定義的realm
DemoRealm demoRealm = new DemoRealm();
//建立安全管理器
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(demoRealm);
//構建 subject
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken();
usernamePasswordToken.setUsername("xiezihao");
usernamePasswordToken.setPassword("123456".toCharArray());
//登入
subject.login(usernamePasswordToken);
if(subject.isAuthenticated()){
System.out.println("使用者已經成功登入");
}
if(subject.hasRole("admin")){
System.out.println("使用者擁有admin角色");
}
if(subject.isPermitted("user.insert")){
System.out.println("使用者擁有user.insert 操作的許可權");
}
}
最後輸出的結果如下
開始使用者登入操作
使用者已經成功登入
開始鑑權操作
使用者擁有admin角色
開始鑑權操作
使用者擁有user.insert 操作的許可權
jdbc realm
前面的操作中我們使用的都是寫死或者本地檔案中寫入使用者配置,但是在實際的過程中我們肯定是從資料庫中讀取的
shiro提供了一個JdbcRealm
的類用來查詢資料庫,但是這個類裡的sql 語句預設都是寫死的,我們可以進入到這個類的原始碼裡去看下
/**
* The default query used to retrieve account data for the user.
*/
protected static final String DEFAULT_AUTHENTICATION_QUERY = "select password from users where username = ?";
/**
* The default query used to retrieve account data for the user when {@link #saltStyle} is COLUMN.
*/
protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY = "select password, password_salt from users where username = ?";
/**
* The default query used to retrieve the roles that apply to a user.
*/
protected static final String DEFAULT_USER_ROLES_QUERY = "select role_name from user_roles where username = ?";
/**
* The default query used to retrieve permissions that apply to a particular role.
*/
protected static final String DEFAULT_PERMISSIONS_QUERY = "select permission from roles_permissions where role_name = ?";
在這個類的開頭定義了 這4個sql 語句,從上到下的用處分別是:
- 使用者賬號密碼登入
- 使用者帶salt的賬號密碼登入
- 查詢使用者名稱下所有的角色
- 查詢角色下所有的許可權
所以我們只需要把資料庫的表按照這個sql 語句的形式來建立就可以了,但是這樣子做肯定是不行的,因為實際的業務欄位名可能不是這樣的,所有我們可以重新自定義sql 語句,按照剛才原始碼裡的格式 實際的操作請看下面的程式碼
@Test
public void test(){
//設定一個數據庫連線池,這裡使用了阿里巴巴的druid連線池
DruidDataSource source = new DruidDataSource();
source.setUrl("jdbc:mysql://localhost:3306/rili");
source.setUsername("root");
source.setPassword("root");
//建立一個jdbcrealm 並加你個連線池設定到這個realm中去
JdbcRealm jdbcRealm = new JdbcRealm();
jdbcRealm.setDataSource(source);
//自定義登入sql
String sql = "select password from user where user_name = ?";
jdbcRealm.setAuthenticationQuery(sql);
//自定義角色sql
String sql2 = "select quan from role where name = ?";
jdbcRealm.setUserRolesQuery(sql2);
//自定義許可權sql
String sql3= "select permission from permissions where juese = ?";
jdbcRealm.setPermissionsQuery(sql3);
/**
* 這裡是很重要的一句!必須要設定他為true,原始碼裡的解釋是預設人false
* 表示只有使用者名稱和角色關聯,如果要角色和許可權關聯必須設定為true
*/
jdbcRealm.setPermissionsLookupEnabled(true);
//之後的操作步驟就是和之前的一樣就不再做複述了
DefaultSecurityManager securityManager = new DefaultSecurityManager();
securityManager.setRealm(jdbcRealm);
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("xiezihao","12345");
subject.login(token);
if(subject.isAuthenticated()){
System.out.println("login ok");
}
subject.checkRoles("admin");
subject.checkPermissions("user.update");
spring 整合shiro
到上面為止,介紹了shiro的基本使用方法,下面講解的是如何將shiro和spring進行整合。
引入依賴
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
建立一個基本的springboot工程,引入必要的web依賴,這裡關於springboot就不再做描述了
編寫自定義realm
public class MyRealm extends AuthorizingRealm {
//授權
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//獲取登入的使用者名稱
String username = (String) principalCollection.getPrimaryPrincipal();
//授權操作,模擬從資料庫獲取授權資訊
if (username.equals("admin")) {
simpleAuthorizationInfo.addRole("admin");
simpleAuthorizationInfo.addStringPermission("user:insert");
} else {
simpleAuthorizationInfo.addRole("default");
}
return simpleAuthorizationInfo;
}
//登入認證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//獲取登入的使用者名稱和密碼
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
String password = new String(token.getPassword());
//認證,這裡應該從資料庫獲取,為了簡便就直接寫了
if (username.equals("admin") && password.equals("12345")) {
return new SimpleAuthenticationInfo("admin", "12345", "MyRealm");
} else {
return null;
}
}
}
編寫shiro 配置類
這裡是重要的一步,定義一個類進行授權的配置