开发者社区> 问答> 正文

sojson出品shiro-demo心得? 400 报错

sojson出品shiro-demo心得? 400 报错

sojson这个不适合初入学习shiro的人,这个只能说可以跑起来,不建议其他公司再此基础做开发
说明如下:最新需要用shiro,因为看了资料思路还是不够清晰就把sojson的shiro-demo项目核心代码从头撸了一遍。同时和 http://www.jianshu.com/p/3afe7606ccbd 作者 z77z 的作品同时拉到一个项目里面进行了对比。每个功能类都反复看了好多次,自己也进行了代码的归并。
天哪,知道发现了什么吗。
sojson,里面存在很多逻辑问题,有非常多冗余的代码,可能是为了看起来比较复杂吧。里面也存在挺多bug,不知道为什么没有提醒作者。
z77z的这个项目使用shiro-redis的插件,代码言简意赅。如果给代码实现技术,逻辑性和使用性综合评级的话,10级最高,给我的感觉 z77z的项目代码9级是有的,sojson的代码在6级。
这个也有可能是sojson开发周期过长里面实现的功能过多造成的。具体的原因就不分析了。代码都不多可以拉下来看一眼就明白了。
项目开源是好事,由衷感谢作者的慷慨与奉献。
由于sojson的作者群是收费的,所以在这里贴一下问题,希望作者能够整理一下代码,给大家带来更多的福利。
不多说了,放下我发现问题比较多的一个类的代码
 

package com.sojson.core.shiro.filter;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import net.sf.json.JSONObject;

import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;

import com.sojson.common.utils.LoggerUtils;
import com.sojson.core.shiro.cache.VCache;
import com.sojson.core.shiro.session.CustomSessionManager;
import com.sojson.core.shiro.session.SessionStatus;
import com.sojson.core.shiro.session.ShiroSessionRepository;
import com.sojson.core.shiro.token.manager.TokenManager;
/**
 *
 * 开发公司:SOJSON在线工具 <p>
 * 版权所有:© www.sojson.com<p>
 * 博客地址:http://www.sojson.com/blog/  <p>
 * <p>
 *
 * 相同帐号登录控制
 *
 * <p>
 *
 * 区分 责任人 日期 说明<br/>
 * 创建 周柏成 2016年6月2日 <br/>
 *
 * @author zhou-baicheng
 * @email  so@sojson.com
 * @version 1.0,2016年6月2日 <br/>
 *
 */
@SuppressWarnings({"unchecked","static-access"})
public class KickoutSessionFilter extends AccessControlFilter {
    //静态注入
    static String kickoutUrl;
    //在线用户
    final static String ONLINE_USER = KickoutSessionFilter.class.getCanonicalName()+ "_online_user";
    //踢出状态,true标示踢出
    final static String KICKOUT_STATUS = KickoutSessionFilter.class.getCanonicalName()+ "_kickout_status";
    static VCache cache;
    
    //session获取
    static ShiroSessionRepository shiroSessionRepository;
    
    
    @Override
    protected boolean isAccessAllowed(ServletRequest request,
            ServletResponse response, Object mappedValue) throws Exception {
        
        HttpServletRequest httpRequest = ((HttpServletRequest)request);
        String url = httpRequest.getRequestURI();
        Subject subject = getSubject(request, response);
        //如果是相关目录 or 如果没有登录 就直接return true
        if(url.startsWith("/open/") || (!subject.isAuthenticated() && !subject.isRemembered())){
            return Boolean.TRUE;
        }
        Session session = subject.getSession();
        Serializable sessionId = session.getId();
        /**
         * 判断是否已经踢出
         * 1.如果是Ajax 访问,那么给予json返回值提示。
         * 2.如果是普通请求,直接跳转到登录页
         */
        Boolean marker = (Boolean)session.getAttribute(KICKOUT_STATUS); // [BUG] 系统中  踢出 的状态写法乱套,估计作者是拼出来的,也懵了。 其他地方用法 SessionStatus sessionStatus = (SessionStatus) session.getAttribute(CustomSessionManager.SESSION_STATUS);
        if (null != marker && marker ) {
            Map<String, String> resultMap = new HashMap<String, String>();
            //判断是不是Ajax请求
            if (ShiroFilterUtils.isAjax(request) ) {
                LoggerUtils.debug(getClass(), "当前用户已经在其他地方登录,并且是Ajax请求!");
                resultMap.put("user_status", "300");
                resultMap.put("message", "您已经在其他地方登录,请重新登录!");
                out(response, resultMap);
            }
            return  Boolean.FALSE;
        }
        
        
        //从缓存获取用户-Session信息 <UserId,SessionId>
        LinkedHashMap<Long, Serializable> infoMap = cache.get(ONLINE_USER, LinkedHashMap.class);
        //如果不存在,创建一个新的
        infoMap = null == infoMap ? new LinkedHashMap<Long, Serializable>() : infoMap;
        
        //获取tokenId
        Long userId = TokenManager.getUserId();
        
        //如果已经包含当前Session,并且是同一个用户,跳过。
        if(infoMap.containsKey(userId) && infoMap.containsValue(sessionId)){  //***[BUG]*** TODO 判断这样写有问题
            //更新存储到缓存1个小时(这个时间最好和session的有效期一致或者大于session的有效期)
            cache.setex(ONLINE_USER, infoMap, 3600); //***[BUG]*** TODO 过期时间设置的是整体在线用户队列的
            return Boolean.TRUE;
        }
        //如果用户相同,Session不相同,那么就要处理了
        /**
         * 如果用户Id相同,Session不相同
         * 1.获取到原来的session,并且标记为踢出。
         * 2.继续走
         */
        if(infoMap.containsKey(userId) && !infoMap.containsValue(sessionId)){
            Serializable oldSessionId = infoMap.get(userId);
            Session oldSession = shiroSessionRepository.getSession(oldSessionId);
            if(null != oldSession){
                //标记session已经踢出
                oldSession.setAttribute(KICKOUT_STATUS, Boolean.TRUE);
                shiroSessionRepository.saveSession(oldSession);//更新session
                LoggerUtils.fmtDebug(getClass(), "kickout old session success,oldId[%s]",oldSessionId);
            }else{
                shiroSessionRepository.deleteSession(oldSessionId); //***[BUG]*** TODO 在session存储中已经是null,此处delete是想干啥
                infoMap.remove(userId);
                //存储到缓存1个小时(这个时间最好和session的有效期一致或者大于session的有效期)
                cache.setex(ONLINE_USER, infoMap, 3600);
            }
            return  Boolean.TRUE;
        }
        
        if(!infoMap.containsKey(userId) && !infoMap.containsValue(sessionId)){
            infoMap.put(userId, sessionId);
            //存储到缓存1个小时(这个时间最好和session的有效期一致或者大于session的有效期)
            cache.setex(ONLINE_USER, infoMap, 3600);
        }
        return Boolean.TRUE;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request,
            ServletResponse response) throws Exception {
        
        //先退出
        Subject subject = getSubject(request, response);
        subject.logout();
        WebUtils.getSavedRequest(request);
        //再重定向
        WebUtils.issueRedirect(request, response,kickoutUrl);
        return false;
    }

    private void out(ServletResponse hresponse, Map<String, String> resultMap)
            throws IOException {
        try {
            hresponse.setCharacterEncoding("UTF-8");
            PrintWriter out = hresponse.getWriter();
            out.println(JSONObject.fromObject(resultMap).toString());
            out.flush();
            out.close();
        } catch (Exception e) {
            LoggerUtils.error(getClass(), "KickoutSessionFilter.class 输出JSON异常,可以忽略。");
        }
    }

    public static void setShiroSessionRepository(
            ShiroSessionRepository shiroSessionRepository) {
        KickoutSessionFilter.shiroSessionRepository = shiroSessionRepository;
    }

    public static String getKickoutUrl() {
        return kickoutUrl;
    }

    public static void setKickoutUrl(String kickoutUrl) {
        KickoutSessionFilter.kickoutUrl = kickoutUrl;
    }
    
    
}

还发现其他问题就不贴代码了。相信作者重构一次代码就好了。

展开
收起
爱吃鱼的程序员 2020-06-01 11:45:13 686 0
1 条回答
写回答
取消 提交回答
  • https://developer.aliyun.com/profile/5yerqm5bn5yqg?spm=a2c6h.12873639.0.0.6eae304abcjaIB

    com.sojson.core.shiro.filter.PermissionFilter 这里面的一段代码应该也是可以优化的
    -----------------------------------------------------------

    HttpServletRequest httpRequest = ((HttpServletRequest)request);
            /**
             * 此处是改版后,为了兼容项目不需要部署到root下,也可以正常运行,但是权限没设置目前必须到root 的URI,
             * 原因:如果你把这个项目叫 ShiroDemo,那么路径就是 /ShiroDemo/xxxx.shtml ,那另外一个人使用,又叫Shiro_Demo,那么就要这么控制/Shiro_Demo/xxxx.shtml
             * 理解了吗?
             * 所以这里替换了一下,使用根目录开始的URI
             */
            
            String uri = httpRequest.getRequestURI();//获取URI
            String basePath = httpRequest.getContextPath();//获取basePath
            if(null != uri && uri.startsWith(basePath)){
                uri = uri.replaceFirst(basePath, "");
            }

    -----------------------------------------------------------

    String uri = ((HttpServletRequest)request).getServletPath (); //用这个就可以了

    ######

    谢谢对此项目的评价。这个项目是断断续续完成的,开始只打算做一个登陆的Demo,没打算开源,后来陆陆续续添加的东西,开始也没打算公开,BUG肯定存在,而且有不少。这个项目的整体开发周期较短,代码有部分是从各个项目粘贴过来的。更新了一个版本后,后来没再更新,由于时间问题。可以再git上提交合并,我来合并即可。

    另外:提到的比较杂,这个是我故意的。就是有的技术点用几个方式实现,可能感觉乱七八糟的技术点都往上堆,但是不是为了看起来复杂。

    关于加群费用这个问题,可以私聊我,我可以拉进去,但是不是为了收费,是为了杜绝广告。要不然一个活跃的2000人群,广告可想而知。

    ######

    而且上述提到的有问题的类,这个属于功能没完成。功能没实现!后来忙就没搞了,自己创业了,根本无暇顾及。看我评论的时间点就知道了!

    2020-06-01 11:45:15
    赞同 展开评论 打赏
问答排行榜
最热
最新

相关电子书

更多
Java Spring Boot开发实战系列课程【第15讲】:Spring Boot 2.0 API与Spring REST Docs实战 立即下载
Java Spring Boot开发实战系列课程【第7讲】:Spring Boot 2.0安全机制与MVC身份验证实战(Java面试题) 立即下载
阿里特邀专家徐雷Java Spring Boot开发实战系列课程(第18讲):制作Java Docker镜像与推送到DockerHub和阿里云Docker仓库 立即下载