shiro 删除用户session_shiro 一个项目多个系统sessionid赋值 (getsession 重载)

Shiro Security是非常不错的Security框架

最近在我的项目中进行相关整合,shiro不难,难就难在如何对已经成熟的系统进行整合

作为相关切入点,我也考虑了很久,整体运用上了如张开涛大佬所说

对于Subject我们一般这么使用:

1、身份验证(login)

2、授权(hasRole*/isPermitted*或checkRole*/checkPermission*)

3、将相应的数据存储到会话(Session)

4、切换身份(RunAs)/多线程身份传播

5、退出

回归标题,正常整合过后,基本可以正确的进行登录与登出

那么开始进行细节休整

大体介绍我们的系统架构是springmvc进行开发,一个项目里分出了两套系统,系统与系统间的区分仅仅只是 通过url路径上的不同,来表现。那么现在就出现了一种情况,系统1基本用户都能登入,而系统2却只有相关权限人才能登入。

表面上视乎能在登陆上做控制,比如login的时候通过权限判断就可以做到。那么这时候考虑的是如果用户之间通过url强行进入呢。

比如系统1用户登录,直接修改url进入系统2。此时属于非法访问。

正常我们会在filter内做过滤,也好做,在相关登录逻辑内对session赋予标记,在filter做过滤就over了,不符合直接logout。

想到这里,作为一个shiro框架使用者是不是感觉shiro貌似没起作用。

于是思考一个比较合理的方案,于是决定当用户登陆系统时,对sessionid进行赋值,系统1则在sessionid前+上相关字符串标记session为系统1用户。系统2则在sessionid前+上相关字符串标记session为系统2用户。

需求清晰,那么进行可行性分。

那么结合shiro中的session管理,开始做起了调查。

首先进行是的shiro如何修改sessionid,这能找却不能满足我的需求。因为市面上的他们都是以单系统,或者双项目双系统进行写的。完全不能符合我想要的。

一般都是这么做的

**

**

上面的解决方案是对不同项目进行不同的jsessionid名的配置

但我一个项目里怎么可能出现两个shiro,不符合我的要求

于是考虑shiro内置处理session我要做点手脚。

首先是查到了sessionid生成器

自定义了一个id生成器

public class SysSessionIdGenerator implements SessionIdGenerator {

@Override

public Serializable generateId(Session session) {

if(session.getAttribute("sysType")!=null){

return session.getAttribute("sysType").toString()+"_"+UUID.randomUUID().toString();

}

return UUID.randomUUID().toString();

}

}

主要实现SessionIdGenerator  generateId的方法。这里可以看见我吧sysType加到uuid前面。

然后就是注入给shiro使用

配置需要加入

sessionDAO 也要记得注入给 sessionManager 这里我就不写了

那么问题又来了,逻辑上不上应该生成session的时候调用吗,那么shiro的session是在什么时候生成的呢。

我翻了翻源码

subject.getSession()

这个get方法实现的

public Session getSession() {

return getSession(true);

}

public Session getSession(boolean create) {

if (log.isTraceEnabled()) {

log.trace("attempting to get session; create = " + create +

"; session is null = " + (this.session == null) +

"; session has id = " + (this.session != null && session.getId() != null));

}

if (this.session == null && create) {

//added in 1.2:

if (!isSessionCreationEnabled()) {

String msg = "Session creation has been disabled for the current subject. This exception indicates " +

"that there is either a programming error (using a session when it should never be " +

"used) or that Shiro's configuration needs to be adjusted to allow Sessions to be created " +

"for the current Subject. See the " + DisabledSessionException.class.getName() + " JavaDoc " +

"for more.";

throw new DisabledSessionException(msg);

}

log.trace("Starting session for host {}", getHost());

SessionContext sessionContext = createSessionContext();

Session session = this.securityManager.start(sessionContext);

this.session = decorate(session);

}

return this.session;

}

可以看出,当你不传参数的时候默认进行调用ture,

简单的 说当getsession(true)时,会判断现在是否有session,如果没有,则新生成一个,有则就用现有的。false则是如果没有,就不生成了返回null。

发现一个问题,当我刚刚进入登入页面的时候,此时shiro已经生成了一个session,于是在登陆校验时候不会生成新的session了。于是考虑了各种办法,比如登入的时候先logout一下等等,当然这些都叫做歪门邪道。后来发现了这么一篇文章

我用了他的方法反正是没成功,系统还变的有点混乱。

仔细一看他的文章中有这么一段:

使用过程中发现Shiro在登录之后不会生成新的Jessionid。这显然会出现 Session_Fixation。

Shiro自己说会在下一个版本1.3 fix这个问题。

我shiro起步是张开涛大大文章里的版本,所以是1.2.2的。尝试性的换个版本,看了下官网的版本是1.3.2

先换了再说。

发现确实登陆之前与之后sessionid变了,看来在1.3.2的时候会在getsession重新获得session。

但是这一点我并不明确,只能推测是这样。

但是这还不是我的正道。重新明确技术细节,发现我需要重载getsession方法,在getsession的时候把sysTpye(系统标记)字符串传递进去。

还是刚刚上面的代码有这么一行

SessionContext sessionContext = createSessionContext();

Session session = this.securityManager.start(sessionContext);

它吧sessionContext传递进去创建了。那么我似乎可以在这里做文章,查阅资料后发现SessionContext继承了Map。那么我就可以直接对它进行put了。

那么继续往下挖掘源码。

这篇挖掘的文章可以看看,我反正看完思路清晰了一点,毕竟自己debug比较混乱。

此时考虑到sessionContext对象还不是最终目标session,那么我赋予的值要么shiro会对其进行全部输出到session里,要么什么也不做

public class SimpleSessionFactory implements SessionFactory {

/**

* Creates a new {@link SimpleSession SimpleSession} instance retaining the context's

* {@link SessionContext#getHost() host} if one can be found.

*

* @param initData the initialization data to be used during {@link Session} creation.

* @return a new {@link SimpleSession SimpleSession} instance

*/

public Session createSession(SessionContext initData) {

if (initData != null) {

String host = initData.getHost();

if (host != null) {

return new SimpleSession(host);

}

}

return new SimpleSession();

}

}

最后扒到这里,它只是取了host 然后赋值,生成simplesession对象。

看来这里是SessionContext的数据终点,那么我systpye也得在这里进行操作了。

查询 SessionFactory相关资料后,发现原来我们自己也可以定义自己的SessionFactory

对象。于是自定义了一个SessionFactory

public class HrsystemSessionFactory implements SessionFactory {

@Override

public Session createSession(SessionContext initData) {

Session session = null;

if (initData != null) {

String host = initData.getHost();

if (host != null) {

session = new SimpleSession(host);

}

if(initData.get("sysType")!=null){

session.setAttribute("sysType", initData.get("sysType"));

}

}else{

session = new SimpleSession();

}

return session;

}

}

这里做的是把sysType的值赋值给session,然后配置文件注入。

那么与刚刚的SysSessionIdGenerator对接上了。

接下来就是重头戏,重载getsession();以上都有废话之嫌,长话短说。

首先拓展suject接口

public interface SysSubject extends Subject {

Session getSession(String sysType);

}

这里我直接继承Subject接口;

public static Subject getSubject() {

Subject subject = ThreadContext.getSubject();

if (subject == null) {

subject = (new Subject.Builder()).buildSubject();

ThreadContext.bind(subject);

}

return subject;

}

Subject实例是使用ThreadLocal模式来获取,若没有则创建一个并绑定到当前线程。此时创建使用的是Subject内部类Builder来创建的,Builder会创建一个SubjectContext接口的实例DefaultSubjectContext,最终会委托securityManager来根据SubjectContext信息来创建一个Subject

上面代码就是前面介绍源码的文章里有讲的。

那么主要的就是需要进行实例化编写了。

这里有点回到原点了,从创建session变成了如何创建subject对象。

但是仔细剖析方法结构,会发现其实subject获取模式与session获取模式是一样的。

但是重写subjectFactory在网络与张开涛里面都没有触及或者很少。这可能需要阅读源码才理解。

代码如下

public class HrsystemSubjectFactory extends DefaultWebSubjectFactory {

public HrsystemSubjectFactory() {

super();

}

public Subject createSubject(SubjectContext context) {

if (!(context instanceof WebSubjectContext)) {

return super.createSubject(context);

}

WebSubjectContext wsc = (WebSubjectContext) context;

SecurityManager securityManager = wsc.resolveSecurityManager();

Session session = wsc.resolveSession();

boolean sessionEnabled = wsc.isSessionCreationEnabled();

PrincipalCollection principals = wsc.resolvePrincipals();

boolean authenticated = wsc.resolveAuthenticated();

String host = wsc.resolveHost();

ServletRequest request = wsc.resolveServletRequest();

ServletResponse response = wsc.resolveServletResponse();

return new HrsystemSubject(principals, authenticated, host, session, sessionEnabled,

request, response, securityManager);

}

/**

* @deprecated since 1.2 - override {@link #createSubject(org.apache.shiro.subject.SubjectContext)} directly if you

* need to instantiate a custom {@link Subject} class.

*/

@Deprecated

protected Subject newSubjectInstance(PrincipalCollection principals, boolean authenticated,

String host, Session session,

ServletRequest request, ServletResponse response,

SecurityManager securityManager) {

return new WebDelegatingSubject(principals, authenticated, host, session, true,

request, response, securityManager);

}

}

主要是在createSubject方法中实例化HrsystemSubject对象将它传递出去。

而这个HrsystemSubject实例化SysSubject接口 与继承WebDelegatingSubject对象

public class HrsystemSubject extends WebDelegatingSubject implements SysSubject{

public HrsystemSubject(PrincipalCollection principals, boolean authenticated,

String host, Session session, boolean sessionEnabled,

ServletRequest request, ServletResponse response,

SecurityManager securityManager) {

super(principals, authenticated, host, session, sessionEnabled, request, response, securityManager);

}

public Session getSession(String type) {

SessionContext sessionContext = createSessionContext();

sessionContext.put("sysType", type);

Session session = this.securityManager.start(sessionContext);

super.session = decorate(session);

return super.session;

}

}

ok那么此时还没完,我们需要把HrsystemSubjectFactory注入到shiro中去。

这里我完全是模仿着session套路感觉注入的了,因为并没有文章这么做。(或者我没看到吧)

于是此时SecurityUtils.getSubject();get的出来的对象就是我们的HrsystemSubject了。

但是这里运用了下父子继承原理,get对象实际是Subject,内部对象的实例其实是HrsystemSubject

那么我们在对其强制装换成我们刚刚定的(SysSubject)SecurityUtils.getSubject();

于是关于getsession的重载就完成了。

那么逻辑上输入getsession(string sysType)那么就可以对sessionid进行我想要的值了。也能做逻辑控制了。运用场景还是挺广的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值