Apache Shiro 是一個強大且易用的 Java 安全框架,提供了認證、授權、加密和會話管理等功能。在 Shiro 中,Realm 是一個核心組件,用于連接應用程序的安全數據(如用戶、角色、權限等)與 Shiro 的安全框架。本文將詳細介紹如何在 Shiro 中實現 Realm 權限認證。
Subject 與 Shiro 交互。SecurityManager 負責管理所有 Subject 的安全操作。Realm 負責從數據源中獲取安全數據(如用戶、角色、權限等),并進行認證和授權。Realm 是 Shiro 與應用程序安全數據之間的橋梁。它負責:
要實現自定義 Realm,需要繼承 AuthorizingRealm 類,并實現以下兩個方法:
doGetAuthenticationInfo: 用于認證。doGetAuthorizationInfo: 用于授權。import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import java.util.HashSet;
import java.util.Set;
public class CustomRealm extends AuthorizingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 獲取用戶名
String username = (String) token.getPrincipal();
// 根據用戶名從數據庫或其他數據源中獲取用戶信息
User user = getUserByUsername(username);
if (user == null) {
throw new UnknownAccountException("用戶不存在");
}
// 返回認證信息
return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 獲取用戶名
String username = (String) principals.getPrimaryPrincipal();
// 根據用戶名從數據庫或其他數據源中獲取用戶的角色和權限
Set<String> roles = getRolesByUsername(username);
Set<String> permissions = getPermissionsByUsername(username);
// 返回授權信息
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setRoles(roles);
authorizationInfo.setStringPermissions(permissions);
return authorizationInfo;
}
// 模擬從數據庫獲取用戶信息
private User getUserByUsername(String username) {
// 這里應該是從數據庫或其他數據源中獲取用戶信息
// 這里為了演示,直接返回一個模擬的用戶
if ("admin".equals(username)) {
return new User("admin", "123456");
}
return null;
}
// 模擬從數據庫獲取用戶角色
private Set<String> getRolesByUsername(String username) {
Set<String> roles = new HashSet<>();
if ("admin".equals(username)) {
roles.add("admin");
}
return roles;
}
// 模擬從數據庫獲取用戶權限
private Set<String> getPermissionsByUsername(String username) {
Set<String> permissions = new HashSet<>();
if ("admin".equals(username)) {
permissions.add("user:create");
permissions.add("user:delete");
}
return permissions;
}
}
在 Shiro 的配置文件中,需要將自定義的 Realm 配置到 SecurityManager 中。
[main]
customRealm = com.example.CustomRealm
securityManager.realms = $customRealm
在應用程序中,可以通過 Subject 進行認證和授權操作。
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
public class ShiroExample {
public static void main(String[] args) {
// 獲取當前用戶
Subject currentUser = SecurityUtils.getSubject();
// 創建認證令牌
UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
try {
// 登錄認證
currentUser.login(token);
System.out.println("認證成功");
// 檢查角色
if (currentUser.hasRole("admin")) {
System.out.println("用戶擁有 admin 角色");
}
// 檢查權限
if (currentUser.isPermitted("user:create")) {
System.out.println("用戶擁有 user:create 權限");
}
} catch (AuthenticationException e) {
System.out.println("認證失敗: " + e.getMessage());
} finally {
// 退出登錄
currentUser.logout();
}
}
}
在實際應用中,用戶、角色和權限信息通常存儲在數據庫中。我們可以通過集成數據庫來實現 Realm 的認證和授權。
假設我們有以下三張表:
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password VARCHAR(50) NOT NULL
);
CREATE TABLE role (
id INT PRIMARY KEY AUTO_INCREMENT,
role_name VARCHAR(50) NOT NULL
);
CREATE TABLE permission (
id INT PRIMARY KEY AUTO_INCREMENT,
permission_name VARCHAR(50) NOT NULL
);
CREATE TABLE user_role (
user_id INT,
role_id INT,
PRIMARY KEY (user_id, role_id)
);
CREATE TABLE role_permission (
role_id INT,
permission_id INT,
PRIMARY KEY (role_id, permission_id)
);
在自定義 Realm 中,我們可以通過 JDBC 或 ORM 框架(如 MyBatis、Hibernate)從數據庫中獲取用戶、角色和權限信息。
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.HashSet;
import java.util.Set;
public class CustomRealm extends AuthorizingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
try (Connection conn = getConnection()) {
PreparedStatement ps = conn.prepareStatement("SELECT password FROM user WHERE username = ?");
ps.setString(1, username);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
String password = rs.getString("password");
return new SimpleAuthenticationInfo(username, password, getName());
} else {
throw new UnknownAccountException("用戶不存在");
}
} catch (Exception e) {
throw new AuthenticationException("數據庫連接失敗", e);
}
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
Set<String> roles = new HashSet<>();
Set<String> permissions = new HashSet<>();
try (Connection conn = getConnection()) {
// 獲取用戶角色
PreparedStatement ps = conn.prepareStatement(
"SELECT r.role_name FROM role r JOIN user_role ur ON r.id = ur.role_id JOIN user u ON ur.user_id = u.id WHERE u.username = ?");
ps.setString(1, username);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
roles.add(rs.getString("role_name"));
}
// 獲取用戶權限
ps = conn.prepareStatement(
"SELECT p.permission_name FROM permission p JOIN role_permission rp ON p.id = rp.permission_id JOIN role r ON rp.role_id = r.id JOIN user_role ur ON r.id = ur.role_id JOIN user u ON ur.user_id = u.id WHERE u.username = ?");
ps.setString(1, username);
rs = ps.executeQuery();
while (rs.next()) {
permissions.add(rs.getString("permission_name"));
}
} catch (Exception e) {
throw new AuthorizationException("數據庫連接失敗", e);
}
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setRoles(roles);
authorizationInfo.setStringPermissions(permissions);
return authorizationInfo;
}
private Connection getConnection() throws Exception {
// 這里應該是從數據庫連接池中獲取連接
// 這里為了演示,直接返回一個連接
return DriverManager.getConnection("jdbc:mysql://localhost:3306/shiro", "root", "password");
}
}
通過實現自定義 Realm,我們可以將 Shiro 與應用程序的安全數據源(如數據庫)連接起來,實現靈活的認證和授權功能。在實際應用中,我們可以根據需求擴展 Realm,集成更多的安全數據源,并優化性能。
Shiro 提供了豐富的 API 和靈活的擴展機制,使得開發者可以輕松地實現復雜的權限管理需求。通過本文的介紹,希望讀者能夠掌握如何在 Shiro 中實現 Realm 權限認證,并在實際項目中應用這些知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。