• shiro遇到问题再记录~


     1.今年3月份使用了shiro搭建springboot+shiro+cas,遇到过一些问题,好在都解决了(https://www.cnblogs.com/thinkingandworkinghard/p/12596443.html)。 这几天公司又有项目要重新搭建,于是决定再重新搭建。之前使用时候没有使用脚手架,不得不重新搭建。 然后一个个的复制粘贴。。。 好在有同事搞了个脚手架的工具,整个目录改了后直接生成项目。改改包名、改改数据库配置之类的,直接新搞了一套

          但是h5那边在开发时候,自己也搭建了一套服务,导致部分配置和之前的不太兼容。原来想着简单改改就行了,然而又遇到了几个问题,都是之前没遇到过的,好在最后都解决了,再次记录下。

      1.跨域问题

          之前也遇到过跨域的问题,不过这次有些不太一样,之前的登录接口是登录后直接通过redirect 到h5的首页地址,但是这次用code码让h5来控制了跳转。下面是区别:

         

      h5那边估计是因为调整了框架的缘故,他们自己控制跳转。这样就和之前的不兼容了,而且发现个问题是这样的: 每次登陆成功后的sessionId 和 其他接口在客户端产生的不一致。也就是登录时候客户端产生了一个cookie,在访问其他接口时候又产生了一个cookie。 原因就是新的项目是h5控制了页面的跳转,登录成功后他们和服务器交互时候产生了新的cookie,这个cookie是客户端的域名下产生的,和服务器的不是一个。可以通过domain的属性查看 

       于是就在后端的接口里面添加了返回值,把后端生成的sessionId返回给了前端。跨域问题仍然是在Nginx里面设置。

    2. 登录顶掉后h5端无法删除cookie的问题

        这个问题是这样的:同一个账号,在不同的电脑登录。我们在代码里面设置了踢掉的功能,就是一个账号不允许同时登录。因为服务器第二次产生的sessionId 会和第一次不同,一般如果使用shiro,当修改了权限之类的,会把服务器的sessionId清空,所以设置了踢掉的功能。然而这次由于是通过code码的方式,发现h5的 sessionId 在第一次登录后会一直清除不掉,在Google下的cookie里面 看了下domain的域名,是后端产生的。第一次登录后后端产生了一个session,第二次在其他电脑登录又产生了一个,这个时候服务器已经清除了sessionId,但是客户端的域名由于是服务器产生的,他们清除不掉。 当第三次登录时候,就会出现登录后仍然提示你需要重新登录。 一直循环,原因就是客户端用的是第一次的cookieId,服务器是新生成的

         产生的原因是shiro的源码里面,有一段redirect的跳转,源码如下:

    FormAuthenticationFilter.java的 onAccessDenied 方法:
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
            if (isLoginRequest(request, response)) {
                if (isLoginSubmission(request, response)) {
                    if (log.isTraceEnabled()) {
                        log.trace("Login submission detected.  Attempting to execute login.");
                    }
                    return executeLogin(request, response);
                } else {
                    if (log.isTraceEnabled()) {
                        log.trace("Login page view.");
                    }
                    //allow them to see the login page ;)
                    return true;
                }
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("Attempting to access a path which requires authentication.  Forwarding to the " +
                            "Authentication url [" + getLoginUrl() + "]");
                }
               //此处会重定向
                saveRequestAndRedirectToLogin(request, response);
                return false;
            }
        }

    protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
       saveRequest(request);
    redirectToLogin(request, response);
    }

    protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
    String loginUrl = getLoginUrl();
    WebUtils.issueRedirect(request, response, loginUrl);
    }

    public static void issueRedirect(ServletRequest request, ServletResponse response, String url) throws IOException {
    issueRedirect(request, response, url, null, true, true);
    }

    public static void issueRedirect(ServletRequest request, ServletResponse response, String url, Map queryParams, boolean contextRelative, boolean http10Compatible) throws IOException {
    RedirectView view = new RedirectView(url, contextRelative, http10Compatible);
    view.renderMergedOutputModel(queryParams, toHttp(request), toHttp(response));
    }

    //renderMergedOutputModel 最后重定向了
     public final void renderMergedOutputModel(
    Map model, HttpServletRequest request, HttpServletResponse response) throws IOException {

    // Prepare name URL.
    StringBuilder targetUrl = new StringBuilder();
    if (this.contextRelative && getUrl().startsWith("/")) {
    // Do not apply context path to relative URLs.
    targetUrl.append(request.getContextPath());
    }
    targetUrl.append(getUrl());
    //change the following method to accept a StringBuilder instead of a StringBuilder for Shiro 2.x:
    appendQueryProperties(targetUrl, model, this.encodingScheme);

    sendRedirect(request, response, targetUrl.toString(), this.http10Compatible);
    }

      

       就是因为这个重定向导致当第三次登录时候,服务器端先产生了sessionId,当被踢掉访问接口时候,他会先重定向一次,重定向的地址就是我们在配置里面的url.默认的是 unauthorized
        @Bean(name = "shiroFilter")
        public PlatformShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
            PlatformShiroFilterFactoryBean shiroFilterFactoryBean = new PlatformShiroFilterFactoryBean();
            Map<String,Filter> filterMap=shiroFilterFactoryBean.getFilters();
            filterMap.put("formAuth",new ShiroFormAuthenticationFilter());
            shiroFilterFactoryBean.setSecurityManager(securityManager);
           //这个是重定向的地址
            shiroFilterFactoryBean.setLoginUrl("/unauthorized");
            shiroFilterFactoryBean.setSuccessUrl(homepageUrl);
    
            //注意此处使用的是LinkedHashMap,是有顺序的,shiro会按从上到下的顺序匹配验证,匹配了就不再继续验证
            //所以上面的url要苛刻,宽松的url要放在下面,尤其是"/**"要放到最下面,如果放前面的话其后的验证规则就没作用了。
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
            filterChainDefinitionMap.put("/nginx.html", "anon");
            filterChainDefinitionMap.put("/common/filesUpload", "anon");
            filterChainDefinitionMap.put("/common/upload", "anon");
            filterChainDefinitionMap.put("/loginoutController/*","anon");
            filterChainDefinitionMap.put("/authManage/*","anon");
            filterChainDefinitionMap.put("/dispatcher/changeStatus", "anon");
            filterChainDefinitionMap.put("/driver/getUnderWayDriver", "anon");
            filterChainDefinitionMap.put("/getAllMerchants", "anon");
            filterChainDefinitionMap.put("/dologin", "anon");
            filterChainDefinitionMap.put("/updateLevel", "anon");
            filterChainDefinitionMap.put("/permission/levelList", "anon");
            filterChainDefinitionMap.put("/mp/car/batchImport", "anon");
            filterChainDefinitionMap.put("/mp/index/merchantIdstatisticsinfo", "anon");
            filterChainDefinitionMap.put("/mp/index/driverrankdaylist", "anon");
            filterChainDefinitionMap.put("/mp/index/statisticsinfo", "anon");
            //注意此处:如果是h5放开的话 会出现跨域问题
            filterChainDefinitionMap.put("/unauthorized", "anon");
            filterChainDefinitionMap.put("/getMsgCode", "anon");
            filterChainDefinitionMap.put("/dologout", "anon");
            filterChainDefinitionMap.put("/logout.html", "logout");
            filterChainDefinitionMap.put("/**", "formAuth");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return shiroFilterFactoryBean;
        }
    

      正是由于这次的重定向,让后端每次被踢掉后产生了新的sessionId,但是服务器端仍然是第一次的,所以一直产生了这个。而我之前三月份的项目,由于都是服务端redirect的,客户端没有产生过这样的问题,

         这个解决方案需要两块:一块是要重写 FormAuthenticationFilter.java 的 onAccessDenied方法,让其不产生重定向;另外需要在配置文件设置,在添加重定向时候禁止生成session。

       我们写的配置如下:

        

    package com.sq.transportmanage.gateway.service.common.shiro.filter;
    
    import com.alibaba.fastjson.JSON;
    import com.sq.transportmanage.gateway.service.common.web.AjaxResponse;
    import com.sq.transportmanage.gateway.service.common.web.RestErrorCode;
    import org.apache.commons.lang3.StringUtils;
    import org.apache.http.HttpStatus;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
    import org.apache.shiro.web.util.WebUtils;
    import org.springframework.beans.factory.annotation.Value;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * @Author:admin
     * @Date:2019/9/6  如果是ajax请求,header头部直接返回403
     * @Description:
     */
    public class ShiroFormAuthenticationFilter extends FormAuthenticationFilter {
    
        @Value(value = "${homepage.url}")
        private String homePageUrl;
    
        public ShiroFormAuthenticationFilter() {
            super();
        }
    
        @Override
        protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
    
            HttpServletResponse rep = toHttp(response);
            HttpServletRequest reque = (HttpServletRequest)request;
            String url=reque.getRequestURI();
            System.out.print("url===================="+url);
            if(StringUtils.isNotBlank(url)&& url.contains("dologin")){
                return true;
            }
            Subject currentLoginUser = SecurityUtils.getSubject();
            if(currentLoginUser.isAuthenticated()){
                return true;
            }else{
                rep.setStatus(200);
                //去掉重定向 修改为返回状态码 前端拦截到登陆页面
                AjaxResponse ajaxResponse = AjaxResponse.fail(RestErrorCode.HTTP_INVALID_SESSION);
                rep.setCharacterEncoding("UTF-8");//设置服务器的编码,默认是ISO-8859-1
                rep.setContentType("text/html; charset = utf-8");//告诉浏览器服务器的编码格式
                rep.getWriter().write(JSON.toJSONString(ajaxResponse));
                return false;
            }
           /* return false;*/
        }
    
    
        /**
         * 是否是ajax请求
         * @param request
         * @return
         */
        private boolean isAjax(HttpServletRequest request){
            String requestHeader = request.getHeader("X-Requested-With");
            return "XMLHttpRequest".equals(requestHeader);
        }
    }

         

        禁止服务器端在重定向时候禁止生成sessionId,这样被踢掉后就不会产生服务器的sessionId

        @Bean("sessionManager")
        public DefaultWebSessionManager sessionManager(RedisSessionDAO sessionDAO, SimpleCookie sessionIdCookie, CacheManager cacheManager) {
            DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
            //session存活时间60分钟
            sessionManager.setGlobalSessionTimeout(Constants.SESSION_REPIRE_TIME);
            /**是否开启删除无效的session对象  默认为true 此处改为不开启*/
            sessionManager.setDeleteInvalidSessions(false);
            /**是否开启定时调度器进行检测过期session 默认为true 此处改为不检测*/
            sessionManager.setSessionValidationSchedulerEnabled(false);
             //自定义监听 fht 不能使用@WebListern的 HttpSessionListerner 因为shiro重写了session 2020-03-05
            Collection<SessionListener> sessionListeners = new ArrayList<>();
            sessionListeners.add(sessionListener());
            sessionManager.setSessionListeners(sessionListeners);
            sessionManager.setSessionDAO(sessionDAO);
            sessionManager.setSessionIdCookieEnabled(true);
            sessionManager.setSessionIdCookie(sessionIdCookie);
            sessionManager.setCacheManager(cacheManager);
            /*添加重定向时候禁止生成session**/
            sessionManager.setSessionIdUrlRewritingEnabled(false);
            return sessionManager;
        }
    

         而且测试发现,这两个条件都不能去掉。理论上登录拒绝已经没有重定向了,这个重定向禁止生成sessionId应该是没必要在设置了。但是测试发现这两个都需要配置。

  • 相关阅读:
    秋意浓浓回成都2月杂记
    验证表单的js代码段
    C#算法小程序(1)
    C#数据结构之单向链表
    Excel VBA编程的常用代码
    我的漫漫系分路
    2007年下半年系统分析师上午试卷及参考答案
    「2014年間休日カレンダー」のご案内
    Eclipse的几点使用技巧
    沧海一粟小组(第一次作业)
  • 原文地址:https://www.cnblogs.com/thinkingandworkinghard/p/14026022.html
Copyright © 2020-2023  润新知