Apache Shiro是什么?
- Apache Shiro是一個強大且易用的Java安全框架,可執行身份驗證(登錄)、授權、密碼學和會話管理
架構圖

組件基本說明:
- subject(主體):接收客戶端賬號密碼,有一系列的認證授權方法
- security manager(安全管理器):管理所有的subject,並使用底層組件完成操作
- authenticator(認證/登錄校驗):對subject的身份進行認證,最簡單可以理解為對用戶的賬號密碼進行校驗
- authorize(授權):subject認證通過后,在訪問功能時需要通過授權器判斷用戶是否有此功能的操作權限
- realm(域):對當前subject的權限管理數據進行封裝(從數據庫中獲取),分別有認證和授權兩個
- session manager(會話管理器):類似於web session域的session管理域
- sessionDAO(會話DAO):shiro session的Dao類
- cache manager(緩存管理器):用於緩存權限信息,提高系統性能
- crypography(密碼學):一套加密api
快速入門案例
數據庫表:
user

user_role

role_permission

maven依賴:
<!-- shiro-core -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 數據庫驅動:mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!-- 連接池:druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
** 數據庫工具類:**
public class DBUtil {
private DBUtil() {}
private static DruidDataSource druidDataSource = null;
static {
druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test");
druidDataSource.setUsername("root");
druidDataSource.setPassword("root");
}
public static DruidPooledConnection getConnection() throws SQLException {
return druidDataSource.getConnection();
}
public static DataSource getDataSource() {
return druidDataSource;
}
}
** 需要認證授權的用戶信息(模擬表填提交的數據):**
public enum Users {
ADMIN("admin", "123456"), TEST("test", "123456");
private String username;
private String password;
Users(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
** 入門案例: **
/**
* 使用shiro提供的SimpleAccountRealm進行認證(不支持自定義授權)
*/
public class SimpleAccountRealmTest {
//備用。用於存放查詢出來的角色名稱
private static String[] userRoles = new String[5];
//創建一個shiro提供的簡單Realm,不支持自定義授權
private static SimpleAccountRealm realm = new SimpleAccountRealm();
//初始化realm信息
static {
try {
DruidPooledConnection connection = DBUtil.getConnection();
//獲取需要認證用戶的user信息
PreparedStatement userPrepareStatement = connection.prepareStatement("select * from user where username=?");
userPrepareStatement.setString(1, Users.ADMIN.getUsername());
ResultSet userRs = userPrepareStatement.executeQuery();
userRs.next(); //username唯一所以只有一個rs
//根據用戶信息(username)查找出對應的role
PreparedStatement rolePrepareStatement = connection.prepareStatement("select * from user_role where username=?");
rolePrepareStatement.setString(1, Users.ADMIN.getUsername());
ResultSet roleRs = rolePrepareStatement.executeQuery();
int i = 0;
while(roleRs.next()) { //可能有多個角色
userRoles[i++] = roleRs.getString("role_name");
}
/**
* 注意:這里的account信息應該是數據庫中的信息
*/
realm.addAccount(userRs.getString("username"), userRs.getString("password"), userRoles); //將該賬號信息設置到realm中
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// 1. 獲取默認的SecurityManager
DefaultSecurityManager sm = new DefaultSecurityManager();
sm.setRealm(realm);
// 2. 設置到工具中
SecurityUtils.setSecurityManager(sm);
// 3. 獲取主體
Subject subject = SecurityUtils.getSubject();
// 4. 創建token
UsernamePasswordToken token = new UsernamePasswordToken(Users.ADMIN.getUsername(), Users.ADMIN.getPassword());
// 5. 認證
subject.login(token);
System.out.println( "是否認證成功: " + subject.isAuthenticated() );
System.out.println( "是否有super這個角色: " + subject.hasRole("super"));
System.out.println( "是否有default這個角色: " + subject.hasRole("default"));
// boolean[] hasRoles = subject.hasRoles(Arrays.asList(userRoles)); //是否同時有super和default這個角色
}
}
shiro提供的realm基本使用
** IniRealm: **
auth.ini:
[users]
admin=123456,super,default
test=123456,default
[roles]
super=user:delete,user:select,user:update,user:insert
default=user:select
code:
/**
* 使用shiro提供的IniRealm進行認證(使用配置文件保存認證授權信息)
*/
public class IniRealmTest {
private static IniRealm iniRealm;
static {
iniRealm = new IniRealm("classpath:auth.ini");
}
public static void main(String[] args) {
// 1. 獲取默認的SecurityManager
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(iniRealm);
// 2. 設置到工具中
SecurityUtils.setSecurityManager(defaultSecurityManager);
// 3. 獲取主體
Subject subject = SecurityUtils.getSubject();
// 4. 創建token
UsernamePasswordToken token = new UsernamePasswordToken(Users.ADMIN.getUsername(), Users.ADMIN.getPassword());
// UsernamePasswordToken token = new UsernamePasswordToken(Users.TEST.getUsername(), Users.TEST.getPassword());
// UsernamePasswordToken token = new UsernamePasswordToken("AAAA", "XXX"); //UnknownAccountException 沒有該賬號
// UsernamePasswordToken token = new UsernamePasswordToken(Users.TEST.getUsername(), "XXX"); //IncorrectCredentialsException 憑證錯誤(密碼錯誤)
// 5. 認證
subject.login(token);
System.out.println( "是否認證成功: " + subject.isAuthenticated() );
System.out.println( "是否有super這個角色: " + subject.hasRole("super") );
System.out.println( "是否有default這個角色: " + subject.hasRole("default") );
// 6. 授權
System.out.println( "是否有user:select權限 " + subject.isPermitted("user:select"));
System.out.println( "是否有user:delete權限 " + subject.isPermitted("user:delete"));
System.out.println( "是否有user:update權限 " + subject.isPermitted("user:update"));
System.out.println( "是否有user:insert權限 " + subject.isPermitted("user:insert"));
}
}
** JdbcRealm: **
/**
* 使用shiro提供的JdbcRealm進行認證(使用數據庫數據)
*/
public class JdbcRealmTest {
private static JdbcRealm jdbcRealm;
static {
jdbcRealm = new JdbcRealm();
jdbcRealm.setDataSource(DBUtil.getDataSource());
jdbcRealm.setPermissionsLookupEnabled(true); // 使用JdbcRealm時要設置權限開關,默認為false
// 自定義SQL語句(如果不設置自定義的SQL語句則按照JdbcRealm內置的模板SQL語句進行數據庫操作,可以進入JdbcRealm查看)
String sql = "select password from user where username = ?";
jdbcRealm.setAuthenticationQuery(sql);
String roleSql = "select role_name from user_role where username = ?";
jdbcRealm.setUserRolesQuery(roleSql);
String permissionSql = "select permission from role_permission where role_name = ?";
jdbcRealm.setPermissionsQuery(permissionSql);
}
public static void main(String[] args) {
// 1. 獲取默認的SecurityManager
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(jdbcRealm);
// 2. 設置到工具中
SecurityUtils.setSecurityManager(defaultSecurityManager);
// 3. 獲取主體
Subject subject = SecurityUtils.getSubject();
// 4. 創建token
UsernamePasswordToken token = new UsernamePasswordToken(Users.ADMIN.getUsername(), Users.ADMIN.getPassword());
// UsernamePasswordToken token = new UsernamePasswordToken(Users.TEST.getUsername(), Users.TEST.getPassword());
// UsernamePasswordToken token = new UsernamePasswordToken("AAAA", "XXX"); //UnknownAccountException 沒有改賬號
// UsernamePasswordToken token = new UsernamePasswordToken(Users.TEST.getUsername(), "XXX"); //IncorrectCredentialsException 憑證錯誤(密碼錯誤)
// 5. 認證
subject.login(token);
System.out.println( "是否認證成功: " + subject.isAuthenticated() );
System.out.println( "是否有super這個角色: " + subject.hasRole("super") );
System.out.println( "是否有default這個角色: " + subject.hasRole("default") );
// 6. 授權
System.out.println( "是否有user:select權限 " + subject.isPermitted("user:select"));
System.out.println( "是否有user:delete權限 " + subject.isPermitted("user:delete"));
System.out.println( "是否有user:update權限 " + subject.isPermitted("user:update"));
System.out.println( "是否有user:insert權限 " + subject.isPermitted("user:insert"));
}
}
自定義Realm(參考JdbcRealm)
** 輔助類: **
/**
* MyRealm的輔助類(可以定義成內部類)
*/
public class UserDaoUtil {
private UserDaoUtil(){ }
private static Connection conn;
static {
try {
conn = DBUtil.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* 根據username獲取SimpleAccount
* @param username
* @param realmName
* @return
*/
public static SimpleAccount getSimpleAccount(String username, String realmName) {
PreparedStatement prepareStatement = null;
ResultSet rs = null;
SimpleAccount account = null;
try {
prepareStatement = conn.prepareStatement("select * from user where username = ?");
prepareStatement.setString(1, username);
rs = prepareStatement.executeQuery();
rs.next();
account = new SimpleAccount(rs.getString("username"), rs.getString("password"), realmName);
} catch (Exception e) {
e.printStackTrace();
}
return account;
}
/**
* 根據username獲取role
* @param username
* @return
*/
public static Set<String> getRoleNamesForUser(String username) {
PreparedStatement prepareStatement = null;
ResultSet rs = null;
Set<String> roles = null;
try {
roles = new HashSet<>();
prepareStatement = conn.prepareStatement("select * from user_role where username = ?");
prepareStatement.setString(1, username);
rs = prepareStatement.executeQuery();
while(rs.next()) {
roles.add( rs.getString("role_name") );
}
} catch (Exception e) {
e.printStackTrace();
}
return roles;
}
/**
* 獲取Permissions
* @param username
* @param roleNames
* @return
*/
public static Set<String> getPermissions(Set<String> roleNames) {
PreparedStatement prepareStatement = null;
ResultSet rs = null;
Set<String> permissions = null;
try {
permissions = new HashSet<>();
for (String roleName : roleNames) {
prepareStatement = conn.prepareStatement("select * from role_permission where role_name = ?");
prepareStatement.setString(1, roleName);
rs = prepareStatement.executeQuery();
while(rs.next()) {
permissions.add(rs.getString("permission"));
}
}
} catch (Exception e) {
e.printStackTrace();
}
return permissions;
}
}
** 自定義Realm: **
public class MyRealmTest {
private static MyRealm myRealm;
static {
myRealm = new MyRealm();
}
public static void main(String[] args) {
// 1. 獲取默認的SecurityManager
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(myRealm);
// 2. 設置到工具中
SecurityUtils.setSecurityManager(defaultSecurityManager);
// 3. 獲取主體
Subject subject = SecurityUtils.getSubject();
// 4. 創建token
UsernamePasswordToken token = new UsernamePasswordToken(Users.ADMIN.getUsername(), Users.ADMIN.getPassword());
// UsernamePasswordToken token = new UsernamePasswordToken(Users.TEST.getUsername(), Users.TEST.getPassword());
// UsernamePasswordToken token = new UsernamePasswordToken("AAAA", "XXX"); //UnknownAccountException 沒有改賬號
// UsernamePasswordToken token = new UsernamePasswordToken(Users.TEST.getUsername(), "XXX"); //IncorrectCredentialsException 憑證錯誤(密碼錯誤)
// 5. 認證
subject.login(token);
System.out.println( "是否認證成功: " + subject.isAuthenticated() );
System.out.println("----------------------------------------------------------------");
System.out.println( "是否有super這個角色: " + subject.hasRole("super") );
System.out.println( "是否有default這個角色: " + subject.hasRole("default") );
System.out.println("----------------------------------------------------------------");
// 6. 授權
System.out.println( "是否有user:select權限 " + subject.isPermitted("user:select"));
System.out.println( "是否有user:delete權限 " + subject.isPermitted("user:delete"));
System.out.println( "是否有user:update權限 " + subject.isPermitted("user:update"));
System.out.println( "是否有user:insert權限 " + subject.isPermitted("user:insert"));
}
}
