文档章节

JFianl整合Shiro

听_风
 听_风
发布于 2016/08/30 17:18
字数 2152
阅读 125
收藏 11

原文:https://michaelzx.github.io/2016/jfinal-shiro-integration/

入门必看

英文好的可以直接看官网教程,英文不好的可以看下开涛的博客《跟我学Shiro》系列

在看教程之前,最好了解想一些shiro的概念:Apache Shiro Terminology

自己入门时踩的坑

一开始的时候,我自己搞了一个JFinal的全局Interceptor,用来做Shiro的权限判断。原因是一开始不了解Shiro,不知道运用ShiroFilter,理所当然地从JFinal的层面进行思考。

当然,在JFinalInterceptor中也是可以做一些权限的判断的,不过顺序上面先要经过ShiroFilter,然后才会到JFinalFilter,真正进入JFinal

现成方案

可以直接拿来用,可以做参考,我在实践的时候,也参考了不少

JFinalShiroPlugin

项目地址:http://git.oschina.net/myaniu/jfinalshiroplugin

JFinal_Authority

项目地址:http://git.oschina.net/jayqqaa12/JFinal_Authority

Dreampie/jfinal-shiro

项目地址:https://github.com/Dreampie/jfinal-shiro

现成方案之个人感受

部分现成方案中使用了Plugin、Interceptor、Annotation的方式来集成Shrio,个人不是非常喜欢,个人感觉有2个痛点:

1、需要好多代码去实现Plugin、Interceptor、Annotation,虽然别人写好了,但是你还是得去了解源码,万一有个坑呢:),毕竟不是什么大而成熟且运用广泛的解决方案

2、你项目里有这么多代码,想想一下

  • 某一天你想知道哪个Controller或Action里面加了Shiro鉴权,哪些没有加
  • 某一天你想知道各个加了Shrio鉴权的Controller或Action所对应的权限表达式是什么
  • 某一天你想知道加一个权限判断,或去掉一个

你得去找,去改,重新编译,重新部署。不管你疯不疯,反正我感觉我会疯的。

我的选择

既然Shiro是专业的那就让它做好该做的。

  1. 在shiro.ini里面,配置自定义Realm+自定义filters+url表达式,来实现自定义的验证方式
  2. 用web来配置:资源、角色、用户、权限

版本

我现在使用是

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>1.2.4</version>
</dependency>

配置web.xml

<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>
  • <listener> 节点定义了一个ServletContextListener,在web应用程序启动的生时候启动Shiro环境(包括shiro的SecurityManager)默认情况下, 这个listener会自动去找我们的WEB-INF/classes/shiro.ini。

  • <filter> 节点定义了主要的ShiroFilter.这个filter被要求去过滤所有进入web应用程序的请求,因此shiro可以在一个请求到达应用程序之前进行必要的身份验证和访问控制。

  • <filter-mapping> 节点确保所有请求类型通过被ShiroFilterare提出(filed)filter-mapping节点一般是不指定dispatcher元素的,但是shiro需要它们都被定义,以便它能够过滤所有可能被web应用执行的不同请求类型。

添加shiro.ini文件

以下是我常用的shiro.ini

[main]
#sessionId相关设定
sessionIdCookie=org.apache.shiro.web.servlet.SimpleCookie
sessionIdCookie.name=jshop-admin-web
sessionIdCookie.path=/
sessionIdCookie.maxAge=1800
sessionIdCookie.httpOnly=true

#sessionManager
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
sessionManager.sessionIdCookie = $sessionIdCookie
sessionManager.sessionIdCookieEnabled = true
sessionManager.globalSessionTimeout = 3600000

#Realm
dbRealm = com.yourdomain.module.shiro.Realm

#Cache
cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager

#---------------
securityManager.sessionManager=$sessionManager
securityManager.realm = $dbRealm
securityManager.cacheManager = $cacheManager

#[filters] 这个已经被取消,并入main中
#自定义验证过滤器
app_auth= com.yourdomain.module.shiro.AuthorizeFilter
#登录页面
app_auth.loginUrl = /auth/login
#未授权页面
app_auth.unauthorizedUrl=/auth/unauthorized

[urls]
/test/** = anon
/public/** = anon
/uploads/** = anon
/passport/* = anon
/** = app_auth

sessionIdCookie、sessionManager

这俩个似乎没啥好说的。

cacheManager

它定义了一个新的缓存管理实例. 缓存在Shiro的构架体系中是一个非常重要的部分 - 它减少了和数据存贮之间持续往返的通讯。这个例子是使用了在单个JVM上比较好使的MemoryConstrainedCacheManager。如果对你的应用是部署在多个服务器(比如服务器集群)的话,你将会想使用一个集群缓存管理器的实现来替代。

realm

它是很关键的一个地方,这是是需要自己实现的。它作为shiro的一个组件,可以让shiro访问到你的系统中的用户、角色、权限等数据。

[urls]

这是非常重要的一个节点,来配置哪些路径映射哪些过滤器来进行鉴权,可以用逗号分开,配置多个过滤器。

这里的anon是shiro内置的一个过滤器,表示不需要进行鉴权。当然还是很多的shiro内置鉴权过滤器.在后面简单介绍下

[filters]

1.2以后,filters被并入[main]节点,如果继续保留也没事儿,只是会出个警告而已。app_auth是我自己实现的一个filter系统中主要使用这个filter进行鉴权。

shiro的内置filters

  • anon
    org.apache.shiro.web.filter.authc.AnonymousFilter
  • authc
    org.apache.shiro.web.filter.authc.FormAuthenticationFilter
  • authcBasic
    org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
  • logout
    org.apache.shiro.web.filter.authc.LogoutFilter
  • noSessionCreation
    org.apache.shiro.web.filter.session.NoSessionCreationFilter
  • perms
    org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
  • port
    org.apache.shiro.web.filter.authz.PortFilter
  • rest
    org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
  • roles
    org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
  • ssl
    org.apache.shiro.web.filter.authz.SslFilter
  • user
    org.apache.shiro.web.filter.authc.UserFilter

自定义realm

com.yourdomain.module.shiro.Realm

需要继承shiro的AuthorizingRealm

public class Realm extends AuthorizingRealm

需要实现2个抽象方法

doGetAuthenticationInfo

主要是在登录的时候,进行用户身份验证

 /**
 * 获取用户验证信息
 * @param authcToken 所需验证的token
 * @return null or 身份信息
 * @throws AuthenticationException 验证异常
 */
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
    UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
    SysAdmin admin = AdminService.getByUsername(token.getUsername());
    if (admin != null) {
        if(!admin.getPassword().equals(String.valueOf(token.getPassword()))){
            throw new AuthenticationException("密码错误");
        }
        Db.update("update sys_admin set loginTime=?,loginCount=loginCount+1 where id=?",new Date(),admin.getId());
        return new SimpleAuthenticationInfo(admin, admin.getPassword(),admin.getUsername());
    } else {
        throw new AuthenticationException("用户不存在");
    }

}

doGetAuthorizationInfo

在第一次鉴权的时候进行调用,获取并保存到chache中(没有配置cache是不是每次都得调用?)

/**
 * 获取用户授权信息
 * @param principals 用户身份
 * @return null or 授权信息
 */
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    SysAdmin userInPrincipal = (SysAdmin) principals.getPrimaryPrincipal();
    //根据用户获取权限
    List<String> stringPermissions = AdminService.getPermissions(userInPrincipal.getId());
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    //stringPermissions结构:
    //user
    //user:list
    //user:add
    //user:edit
    //...
    info.addStringPermissions(stringPermissions);
    return info;
}

我这里返回的:字符串权限表达式(字符串通配符权限),对于各自所对应的资源(主要就是url路径),我是保存在数据库中,方便进行配置,然后再加上缓存。在自定义filter中将url转换成对应的表达式,然后进行鉴权。

登录、注销

登录

public void login() {
    if(Boolean.FALSE.equals(validateCaptcha("captcha"))){
        renderJson(CommonService.ajaxError("验证码错误"));
        return;
    }
    String username = getPara("username");
    String password = HashKit.md5(getPara("password"));
    Subject subject = SecurityUtils.getSubject();
    UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    try {
        subject.login(token);
        renderJson(CommonService.ajaxSuccess());
    } catch (AuthenticationException e) {
    	//虽然在realm中有具体的错误信息,但是安全起见,统一返回登录失败
        renderJson(CommonService.ajaxError("登陆失败"));
    }
}

注销

public void logout() {
    Subject subject = SecurityUtils.getSubject();
    subject.logout();
    redirect("/passport/login");
}

是不是感觉很easy:)

自定义filter

com.yourdomain.module.shiro.AuthorizeFilter

继承shiro的AuthorizationFilter

public class AuthorizeFilter extends AuthorizationFilter 

实现抽象方法isAccessAllowed

protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
    //-----------------用户验证------------------
    Subject currentUser = getSubject(request, response);
    if (!currentUser.isAuthenticated())
        return false;
    //-----------------获取资源权限表达式-------------
    SysAdmin user = (SysAdmin) currentUser.getPrincipal();
    //request中加入attribute便于controller调用admin的信息
    request.setAttribute("admin",user);
    // 根据actionKey分析出权限表达式
    HttpServletRequest hsr = ((HttpServletRequest) request);
    String root = hsr.getContextPath();
    String URI = hsr.getRequestURI();
    String actionKey = URI.replace(root,"");
    if("".equals(actionKey))
        actionKey="/";
    RoleService roleService = new RoleService();
    String expression = roleService.getActionKeyExpression(actionKey);
    //-----------------进行鉴权-------------
    if (user==null)
        return false;
    else if(user.getStr("username").equals("superadmin")){
    	//超级管理员具有所有权限
        return true;
    }else if(expression==null){
        return false;
    }else if(currentUser.isPermitted(expression)){
    	//鉴权
        return true;
    }else{
        return false;
    }

}
  1. 在这里,我根据actionKey分析出所对应的权限表达式
  2. 对于用户superadmin,默认具有所有权限,等同于,不需要鉴权
  3. 其他用户根据权限表达式,来进行鉴权

关于用户、角色、url资源及对应的权限表达式

你可以按照你自己的方式来构建一套,我相信对于大多数人应该不成问题,因为这个已经不属于shiro的范畴了。自己搞几个表,搞几个配置界面,做下缓存策略等等。

模板引擎中使用扩展函数(标签)

有时候你的应用也许需要在界面上进行鉴权,比如按钮啥的,这时候就可能需要扩展模板引擎的函数或者标签。

可以参考下 《beetl 和 shrio 结合》 http://my.oschina.net/xiandafu/blog/143109

只要解决如何扩展模板引擎的函数或者标签,其他,我想应该都是雷同的吧。

本文转载自:https://michaelzx.github.io/2016/jfinal-shiro-integration/

听_风
粉丝 20
博文 41
码字总数 21207
作品 1
私信 提问
Jfinal plugin,angular--jfinal-angular-icedog

一个Jfinal+angular的框架实现,属于jfinal-dreampie的一个demo,在线访问:http://icedog.jd-app.com/ 欢迎收藏 使用jfinal框架开源工具包jfinal-dreampie,http://search.maven.org/ 输入j...

Dreampie
2014/08/12
7.7K
3
jfinal整合beetl的问题

1 beetl中引用的jfinal的PathUtil, 目前jfianl已经改为PathKit. 我是复制了代码在我的jfianl-ext中改了重编译了一下 :) 2 分页宏的问题 如果用freemaker这里是可以引入这个${PATH}的,但是用b...

绝望的八皮
2013/01/14
3.3K
6
关于JFinal 整合 redis 有序集合 的交集问题

@JFianl 波总你好,我在用JFianl 的 redis 的时候,发现少了 一些 方法,比如 有序集合的 求交集的方法,我自己在Cache类里面增加了一些方法,但是就是不生效,求解救。 以下是代码 //交集:...

草帽路飞
2016/01/02
436
0
jfinal什么时候支持oracle呢?期待

看了看jfianl的代码,简洁!不过为什么不直接支持oracle,还有存储过程呢?期待着jfianl的下一版本!

无聊的人们啊
2012/03/24
709
5
JFinal集成Maven启动不成功

下载了 JFianl的微信JFianl Maven demo,可以运行成功,但是我自己建的Maven项目与JFianl集成不成功,求指导:目录结构如下: 启动方式为 报错404 如果改成 src/main/webapp 8080 / 5 报错5...

默默积极
2014/12/03
2.1K
2

没有更多内容

加载失败,请刷新页面

加载更多

centos 设置hostname

1、centos 6 hostname配置文件为 /etc/sysconfig/network 内容为 NETWORKING=yesHOSTNAME=test_hostname hostname是Linux系统下的一个内核参数,它保存在 /proc/sys/kernel/hostname 下,但......

燃犀
35分钟前
1
0
openSUSE

opensuse aliyun镜像 sudo zypper addrepo -f https://mirrors.aliyun.com/opensuse/distribution/leap/15.0/repo/oss/ aliyun-Oss sudo zypper addrepo -f https://mirrors.aliyun.com/ope......

李有常
41分钟前
1
0
五月天 知足 [結他chord譜]

詞曲: 阿信 Key:E 4/4 Arranged By Kermit Tam Capo 4 > Play C |C   G  |Am  Em  (怎)麼去(擁)有 (一)道彩(虹)  |F   C |F     G (怎)麼去(擁)抱 (一)夏天的(風) |...

阿锋zxf
47分钟前
2
0
OSChina 周二乱弹 —— 你醒啦?现在你已经是丧尸了

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @小鱼丁 :分享全仁权的单曲《걱정말아요 그대 (你不要担心)》: 《걱정말아요 그대 (你不要担心)》- 全仁权 手机党少年们想听歌,请使劲儿戳(...

小小编辑
48分钟前
1K
13
再一次生产 CPU 高负载排查实践

前言 前几日早上打开邮箱收到一封监控报警邮件:某某 ip 服务器 CPU 负载较高,请研发尽快排查解决,发送时间正好是凌晨。 其实早在去年我也处理过类似的问题,并记录下来:《一次生产 CPU ...

crossoverJie
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部