zoukankan      html  css  js  c++  java
  • 禁止ajax访问shiro管理的登录页面

    在使用shiro的时候,对于用户权限的管理,相信很多人都已经很熟悉了。
    今天,我这里简单的记录一下我自己调试过程中遇到的问题。主要是登录的操作,禁止通过ajax的方式进行访问。

    shiro中,登录过程拒绝ajax的访问操作,主要在FormAuthenticationFilter里面实现的。更具体的说,应该是自己重写AccessControlFilter方法中的onAccessDenied方法。

    为什么要禁止ajax的登录?安全的考虑!

    先看看自己应用中重写的RdFormAuthenticationFilter,它继承于FormAuthenticationFilter,至于FormAuthenticationFilter和AccessControlFilter的关系,自己看shiro的源码吧。

    public class RdFormAuthenticationFilter extends FormAuthenticationFilter {
    
        private String passwordParam;
    
        @Autowired
        @Qualifier("mqmKefuService")
        private IMqmKefuService mksService;
        
        @Override
        protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue)
                throws Exception {
            if (request.getAttribute(getFailureKeyAttribute()) != null) {
                return true;
            }        
    
            HttpServletRequest httpServletRequest = (HttpServletRequest)request;
            if (isLoginSubmission(request, response)) {
                return executeLogin(request, response);
            } else {
                if ("XMLHttpRequest".equalsIgnoreCase(httpServletRequest.getHeader("X-Requested-With")) 
                        || request.getParameter("ajax") != null) {
                    HttpServletResponse res = WebUtils.toHttp(response);
                    res.sendError(HttpServletResponse.SC_UNAUTHORIZED);
                }
                return true;
            }        
        }
    
        @Override
        protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
                    ServletRequest request, ServletResponse response) throws Exception {        
            HttpServletRequest httpServletRequest = (HttpServletRequest)request;
            HttpServletResponse httpServletResponse = (HttpServletResponse)response;
            
            String username = (String)SecurityUtils.getSubject().getPrincipal();
            if(username == null){
                return true;
            }
            
            MqmKefu user = mksService.selectByUsername(username);
            
            SecurityUtils.getSubject().getSession().setAttribute(Constants.CURRENT_USER, user);        
                    
            if ("XMLHttpRequest".equalsIgnoreCase(httpServletRequest.getHeader("X-Requested-With")) 
                    || request.getParameter("ajax") != null) {
                // 是ajax请求
                httpServletRequest.getRequestDispatcher("/login/timeout/success").forward(httpServletRequest, httpServletResponse);
            } else {
                //httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + this.getSuccessUrl());
                String redirectUrl = httpServletRequest.getContextPath() + "/home";
                SavedRequest sr = WebUtils.getSavedRequest(request);
                //当用户地址的请求不是在地址栏输入,而是在页面上直接点击登录,那么SavedRequest返回值将会是空的。
                if(sr != null) {
                    redirectUrl = sr.getRequestUrl();                
                }
                httpServletResponse.sendRedirect(redirectUrl);
            }
            
            return false;
        }
    
        public String getPasswordParam() {
            return passwordParam;
        }
    
        public void setPasswordParam(String passwordParam) {
            this.passwordParam = passwordParam;
        }
    
    }

    AAAA。这里,重点是下面的这个代码片段:

    if ("XMLHttpRequest".equalsIgnoreCase(httpServletRequest.getHeader("X-Requested-With")) 
            || request.getParameter("ajax") != null) {
        HttpServletResponse res = WebUtils.toHttp(response);
        res.sendError(HttpServletResponse.SC_UNAUTHORIZED);
    }

    如何测试呢?很简单!
    1. 将shiro的配置中,重写LogoutFilter,并将其redirectUrl的值配置为自己想要的内容,而不是原来默认的DEFAULT_REDIRECT_URL(值为/)。例如,将redirectUrl改为登录的url,这里是/usr/login.
    2. 将前端页面的退出,一种用ajax的post或者get方式触发,一种用href直接配置url。
    2.1 针对退出url (/usr/logout)以ajax的方式触发事件到后台,代码逻辑将会进入上述代码AAAA,且此时HTTP请求头部含有X-Requested-With。前端将收到401的错误,页面不跳转。
    2.2 针对退出url以a标签的href方式,直接由浏览器以http调用后台服务的方式,则不会进入AAAA的代码逻辑。

    针对上述的LogoutFilter的重写,可以看看配置文件:

        <bean id="sysUserFilter" class="com.roomdis.mqr.infra.shiro.SysUserFilter"/> 
        <bean id="sysLogoutFilter" class="com.roomdis.mqr.infra.shiro.RdLogoutFilter">
            <property name="redirectUrl" value="/user/login"/>
        </bean>
        
        <bean id="jCaptchaValidateFilter" class="com.roomdis.mqr.infra.shiro.JCaptchaValidateFilter">
            <property name="jcaptchaEbabled" value="true"/>
            <property name="jcaptchaParam" value="jcaptchaCode"/>
            <property name="failureKeyAttribute" value="shiroLoginFailure"/>
        </bean>
        
        <bean id="filterChainManager" class="com.roomdis.mqr.infra.shiro.RdDefaultFilterChainManager">
            <property name="loginUrl" value="/user/login" />        
            <property name="successUrl" value="/home" />         
            <property name="unauthorizedUrl" value="/unauth" />  
            <property name="customFilters">
                <util:map>                
                    <entry key="authc" value-ref="authcFilter"/>
                    <entry key="sysUser" value-ref="sysUserFilter"/>
                    <entry key="jCaptchaValidate" value-ref="jCaptchaValidateFilter"/>
                    <entry key="logout" value-ref="sysLogoutFilter" />
                </util:map>
            </property>
            <property name="defaultFilterChainDefinitions">  
                <value>
                    / = anon
                    /*.png = anon
                     /css/* = anon
                    /js/** = anon
                    /image/* = anon
                    /index.html = anon
                    /user/login = jCaptchaValidate,authc                        
                    /jcaptcha* = anon
                    /jcaptcha.jsp = anon
                    /home = sysUser,user                                                 
                    /logout = logout                
                    <!-- /home = authc, perms[/home]  perms 表示需要该权限才能访问的页面 -->  
                </value>
            </property>
        </bean> 

    上述配置的红色部分,是重写LogoutFilter的对应bean的信息。

    蓝色部分,要特别注意,logout的url,权限部分处理,必须是和自己代码逻辑中的重定向部分一致使用。例如这里的/logout,对应代码逻辑中的地方(红色)如下:

        /**
         * logout 即常说的登出操作
         * 
         * @param req
         * @param rsp
         * @param model
         * @return
         */
        @GET
        @Path("/user/logout")    
        @Produces(MediaType.APPLICATION_JSON)
        public void logout(@Context HttpServletRequest req, @Context HttpServletResponse rsp){
            Map<String, Object> infoMap = new HashMap<String, Object>();
            String basePath = basePath(req);
            String usernamep = req.getParameter("username");
            infoMap.put("basePath", basePath);
            SecurityUtils.getSubject().getSession().removeAttribute(Constants.CURRENT_USER);        
            try {
                rsp.sendRedirect(req.getContextPath() + "/logout");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    前端的代码:

    <p class="checkmove">
         <span class="fl sp_title">
            <#if username??>
                <label id="username">${username}</label>
                <a class=logout-btn href="${basePath}/user/logout">退出</a>         #能够正常运行,即不会触发401的错误的用法
                <!--                          
                <button class=button id="leave">退出</button>                       #这样子用,配合后台的ajax的方式触发,就会出现401的错误
                 -->
            </#if>                    
            <select>
                <option value="0">上线</option>
                <option value="1">离开</option>
                <option value="2">隐身</option>
                <option value="3">下线</option>
            </select>
        </span>
    </p>

    下面上一个图,看看我的项目雏形!

  • 相关阅读:
    pymysql模块操作数据库及连接报错解决方法
    lvs负载均衡
    redis(nosql数据库)
    zabbix
    shell正则表达式
    红帽CentOS7 密码破解
    shell基础及变量符号
    xshell连接虚拟机
    散列表与哈希算法学习笔记
    LeetCode-300 最长上升子序列
  • 原文地址:https://www.cnblogs.com/shihuc/p/7553669.html
Copyright © 2011-2022 走看看