Jfinal與shiro整合實現動態URL鑑權,不裝外掛只需要一個類
阿新 • • 發佈:2019-02-08
Jfinal與Shiro整合,有瑪雅牛和dreamip兩個Jfinal外掛,但還是想以簡單的方式實現動態URL鑑權。
本人的實現思路是,利用Shiro本身的過濾器擴充套件來實現動態通過資料庫URL授權。方法如下:
1. 新建一個JFinal Maven專案
2. pom.xml中新增對Shiro的引用:
3. /WEB-INF/web.xml中加入shiro的支援<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-all</artifactId> <version>1.2.3</version> </dependency>
<listener> <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class> </listener> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping>
4. resource目錄下新增配置檔案shiro.ini
其中[main]節中,jdbcRealm定義一個數據庫認證域,根據那幾個SQL,可以構造出相應的資料表。定義了一個MD5的密碼加密演算法,在[filters]節自定義了一個過濾ShiroPathMatchFilter,並且在[urls]節將其它非開放的URL鑑權都指向它。[main] shiro.loginUrl = /auth/login jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm dataSource = com.mysql.jdbc.jdbc2.optional.MysqlDataSource dataSource.serverName = localhost dataSource.user = root dataSource.password = root dataSource.databaseName = jinlu jdbcRealm.dataSource = $dataSource jdbcRealm.authenticationQuery = SELECT password FROM sec_user WHERE status=1 AND username = ? jdbcRealm.userRolesQuery = SELECT r.role_name FROM sec_role AS r, sec_user_role AS ur WHERE r.id = ur.role_id AND r.status=1 AND ur.user_id = (SELECT id FROM sec_user WHERE username = ?) jdbcRealm.permissionsQuery = SELECT p.permission FROM sec_permission AS p, sec_role_permission AS rp WHERE p.id = rp.permission_id AND rp.role_id = (SELECT id FROM sec_role WHERE role_name = ?) jdbcRealm.permissionsLookupEnabled = true securityManager.realms = $jdbcRealm passwordMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher passwordMatcher.hashAlgorithmName=MD5 jdbcRealm.credentialsMatcher=$passwordMatcher #cache shiroCacheManager = org.apache.shiro.cache.ehcache.EhCacheManager shiroCacheManager.cacheManagerConfigFile = classpath:ehcache.xml securityManager.cacheManager = $shiroCacheManager #session sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager sessionDAO.activeSessionsCacheName = shiro-activeSessionCache sessionManager.sessionDAO = $sessionDAO securityManager.sessionManager = $sessionManager securityManager.sessionManager.globalSessionTimeout = 3600000 [filters] urlFilter=com.ziyTech.framework.interceptor.ShiroPathMatchFilter #這裡的規則,web.xml中的配置的ShiroFilter會使用到。 [urls] /=anon /img/**=anon /js/**=anon /css/**=anon /fonts/**=anon /uploader/**=anon /uploads/**=anon /auth/**=anon /msg/**=anon /api/**=anon /test/*=anon /**=urlFilter
5. ShiroPathMatchFilter實現對URL進行過濾
package com.ziyTech.framework.interceptor;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Multimap;
import com.jfinal.log.Log;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.Record;
import com.ziyTech.framework.service.Conf;
import com.ziyTech.framework.model.SecRole;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.*;
public class ShiroPathMatchFilter extends AccessControlFilter {
private static final Log log = Log.getLog(Conf.class);
private static Multimap<String,String> allPermissions = ArrayListMultimap.create();
private static List<String> allRoles = new ArrayList<String>();
public static void initUrlMaps(){
log.info("start initializing permission maps.");
// 快取所有角色
allRoles.clear();
List<SecRole> secRoles =SecRole.dao.findAll();
for(SecRole secRole:secRoles){
allRoles.add(secRole.getStr("role_name"));
}
// 快取所有許可權
allPermissions.clear();
List<Record> rolePermissions = Db.find("select r.role_name,p.permission " +
"from sec_role r,sec_permission p,sec_role_permission rp " +
"where rp.role_id=r.id and rp.permission_id=p.id and permission is not null ");
for(Record rolePermission :rolePermissions){
allPermissions.put(rolePermission.getStr("role_name"),rolePermission.getStr("permission"));
}
log.info("finished permissions map with entries:" + allPermissions.size());
}
public boolean isAccessAllowed(Subject subject,String path){
if(allPermissions.isEmpty()){
initUrlMaps();
}
for(String role : allRoles){
if(subject.hasRole(role)){
for(String url:allPermissions.get(role)){
if(pathsMatch(url, path)){
return true;
}
}
}
}
return false;
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object o) throws Exception {
if(allPermissions.isEmpty()){
initUrlMaps();
}
Subject subject = getSubject(request, response);
for(String role : allRoles){
if(subject.hasRole(role)){
for(String url:allPermissions.get(role)){
if(pathsMatch(url, request)){
return true;
}
}
}
}
return false;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
log.info("onAccessDenied");
setLoginUrl("/auth/login");
redirectToLogin(request,response);
return false;
}
}
上述程式碼中靜態變數allPermissions為MultiMap, 引這Google guava。有靜態方法initUrlMaps,可在其它地方對授權資訊進行初始化,如使用者更改了角色,角色更改了許可權時。
6. 我的鑑權相關資料庫定義:
CREATE TABLE sec_user (
id INT NOT NULL AUTO_INCREMENT,
username VARCHAR(50),
password VARCHAR(50),
email VARCHAR(100),
mobile VARCHAR(20),
avatar VARCHAR(200),
full_name VARCHAR(100),
status INT DEFAULT '1' NOT NULL,
created_at TIMESTAMP NULL,
updated_at TIMESTAMP NULL,
deleted_at TIMESTAMP NULL,
PRIMARY KEY (id)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8
CREATE TABLE sec_role (
id INT NOT NULL AUTO_INCREMENT,
role_name VARCHAR(50),
description VARCHAR(200),
status INT DEFAULT '1' NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE
CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL,
deleted_at TIMESTAMP NULL,
PRIMARY KEY (id)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8
CREATE TABLE sec_permission (
id INT NOT NULL AUTO_INCREMENT,
permission VARCHAR(50) NOT NULL,
description VARCHAR(200) NOT NULL,
status INT DEFAULT '1' NOT NULL,
category VARCHAR(50),
name VARCHAR(50),
url VARCHAR(50),
PRIMARY KEY (id)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8
CREATE TABLE sec_user_role (
id INT NOT NULL AUTO_INCREMENT,
user_id INT NOT NULL,
role_id INT NOT NULL,
PRIMARY KEY (id)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8
CREATE TABLE sec_role_permission (
id INT NOT NULL AUTO_INCREMENT,
role_id INT NOT NULL,
permission_id INT NOT NULL,
PRIMARY KEY (id)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8
7. 使用方法
7.1 sec_permission表中,如圖定義許可權,指定URL pattern
7.2 通過sec_role_permission將多個許可權賦予一個角色
7.3 通過sec_user_role給使用者賦予角色,以實現授權