zoukankan      html  css  js  c++  java
  • SpringBoot+Shiro放行OPTIONS请求,解决跨域问题

    问题:

    集成shiro之后发现配置放行的接口可以正常访问,而需要登录验证的接口会报错

    其中OPTIONS类型的接口会报302

    导致后续的post请求报错提示跨域问题


    Shiro登录流程

    首先Shiro是根据请求中cookie携带的JSESSIONID判断是否登录的
    当调用登录接口登录成功时,后端的响应头会添加一个set-cookie的参数

    JSESSIONID代表当前登录的用户,前端只要在请求中携带这个参数Shiro就可以识别出用户并放行。
    但是post请求会先发送一个OPTIONS类型的探测请求,由于这个请求没有携带JSESSIONID,Shiro就会判断为未登录,进行拦截。
    所以解决的思路就是放行OPTIONS类型的请求。

    解决

    1. 创建ShiroUserFilter
    package com.school.service.config;
    
    import com.school.service.entity.Result;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.shiro.web.filter.authc.UserFilter;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.RequestMethod;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    @Slf4j
    public class ShiroUserFilter extends UserFilter {
        /**
         * 在访问过来的时候检测是否为OPTIONS请求,如果是就直接返回true
         */
        @Override
        protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
                log.info("OPTIONS放行");
                setHeader(httpRequest,httpResponse);
                return true;
            }
            return super.preHandle(request,response);
        }
    
        /**
         * 该方法会在验证失败后调用,这里由于是前后端分离,后台不控制页面跳转
         * 因此重写改成传输JSON数据
         */
        @Override
        protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
            saveRequest(request);
            setHeader((HttpServletRequest) request,(HttpServletResponse) response);
            PrintWriter out = response.getWriter();
            //自己控制返回的json数据
            out.println(new Result("500",null,"Shiro验证失败"));
            out.flush();
            out.close();
        }
    
        /**
         * 为response设置header,实现跨域
         */
        private void setHeader(HttpServletRequest request, HttpServletResponse response){
            //跨域的header设置
            response.setHeader("Access-control-Allow-Origin", request.getHeader("Origin"));
            response.setHeader("Access-Control-Allow-Methods", request.getMethod());
            response.setHeader("Access-Control-Allow-Credentials", "true");
            response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
            //防止乱码,适用于传输JSON数据
            response.setHeader("Content-Type","application/json;charset=UTF-8");
            response.setStatus(HttpStatus.OK.value());
        }
    }
    
    
    1. 在Shiro配置类的shiroFilter中添加进去
    package com.school.service.config;
    
    import org.apache.shiro.mgt.DefaultSecurityManager;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import javax.servlet.Filter;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * Shiro配置类
     */
    @Configuration
    public class ShiroConfig {
    
        @Bean(name = "shiroFilter")
        public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            shiroFilterFactoryBean.setLoginUrl("/school/goToLogin");//设置登录页面
            shiroFilterFactoryBean.setUnauthorizedUrl("/school/goToLogin");//权限不足跳转页面,这个在Default过滤器中设置无效,具体看 https://blog.csdn.net/bicheng4769/article/details/86680955
    
            //添加自定义Filter,放行OPTIONS请求-----------------------------------------这里✨✨✨✨✨✨✨✨
            Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
            filters.put("authc", new ShiroUserFilter());
            shiroFilterFactoryBean.setFilters(filters);
            //----------------------------------------------------------------------------------------------✨
    
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
            // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
            filterChainDefinitionMap.put("/service/school/**", "anon");
            filterChainDefinitionMap.put("/swagger-ui.html", "anon");
            filterChainDefinitionMap.put("/swagger-resources", "anon");
            filterChainDefinitionMap.put("/v2/api-docs", "anon");
            filterChainDefinitionMap.put("/webjars/springfox-swagger-ui/**", "anon");
            filterChainDefinitionMap.put("/configuration/security", "anon");
            filterChainDefinitionMap.put("/configuration/ui", "anon");
    
            filterChainDefinitionMap.put("/service/article/**", "authc");
            filterChainDefinitionMap.put("/service/chat/**", "authc");
            filterChainDefinitionMap.put("/service/diary/**", "authc");
            filterChainDefinitionMap.put("/service/file/**", "authc");
            filterChainDefinitionMap.put("/service/problem/**", "authc");
            filterChainDefinitionMap.put("/service/team-article/**", "authc");
            filterChainDefinitionMap.put("/service/team/**", "authc");
            filterChainDefinitionMap.put("/service/user/**", "authc");
            filterChainDefinitionMap.put("/service/user-friend/**", "authc");
            filterChainDefinitionMap.put("/service/user-info/**", "authc");
            filterChainDefinitionMap.put("/service/widget/**", "authc");
    
            //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证
            filterChainDefinitionMap.put("/**", "anon");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return shiroFilterFactoryBean;
        }
    
        @Bean
        public SecurityManager securityManager(){
            DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
            defaultWebSecurityManager.setRealm(myRealm());
            return defaultWebSecurityManager;
        }
    
        @Bean
        public MyRealm myRealm (){
            MyRealm myRealm = new MyRealm();
            return myRealm;
        }
    
    
    }
    
    

    一切正常

  • 相关阅读:
    红蓝对抗
    SQLMAP用法大全
    Web安全工程师(进阶)课程表
    msf连接PostgreSQL数据库
    我的web安全工程师学习之路——规划篇
    web安全深度剖析pdf
    js面试题
    js克隆一个对象
    js面试必考:this
    前端面试:js数据类型
  • 原文地址:https://www.cnblogs.com/charlottepl/p/15599032.html
Copyright © 2011-2022 走看看