Jfinal与shiro集成实现动态URL鉴权,不装插件只需要一个类

Jfinal与Shiro集成,有玛雅牛和dreamip两个Jfinal插件,但还是想以简单的方式实现动态URL鉴权。

本人的实现思路是,利用Shiro本身的过滤器扩展来实现动态通过数据库URL授权。方法如下:

1. 新建一个JFinal Maven项目

2. pom.xml中添加对Shiro的引用:

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-all</artifactId>
            <version>1.2.3</version>
        </dependency>
3. /WEB-INF/web.xml中加入shiro的支持

    <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]
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
其中[main]节中,jdbcRealm定义一个数据库认证域,根据那几个SQL,可以构造出相应的数据表。定义了一个MD5的密码加密算法,在[filters]节自定义了一个过滤ShiroPathMatchFilter,并且在[urls]节将其它非开放的URL鉴权都指向它。

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给用户赋予角色,以实现授权

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值