zoukankan      html  css  js  c++  java
  • SpringSecurity基础场景应用大全

    SpringSecurity应用

    pring Security 功能简介

    1. 认证:用户登录,两种认证方式:httpBasic、formLogin
    2. 授权:判断用户权限,可以访问什么资源
    3. 安全防护,防止跨站请求,session攻击等。

    应用场景:

    1. 登录
    2. 授权
    3. 单一登录,一个账户同一时间只能在一个地方登录
    4. 集成cas,单点登录
    5. 集成oauth2,可以做第三方登录

    基础入门

    1. 引入依赖:
     <!--添加Spring Security 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    
    1. 再写一个简单的controller
    @RestController
    public class HelloController {
    
    
        @RequestMapping("/hello")
        public String hello(){
            return "hello security";
        }
    }
    
    1. 访问http://localhost:8080/hello,发现自动跳到了登录页面。

    1. 进行登录,默认用户名:user,密码看启动日志
    Using generated security password: bbb788d7-13aa-4e6d-9dc6-b7923a27e6a9
    

    SpringSecurity认证基本原理

    在使用SpringSecurity框架,该框架会默认自动地替我们将系统中的资源进行保护,每次访问资源的
    时候都必须经过一层身份的校验,如果通过了则重定向到我们输入的url中,否则访问是要被拒绝的。那
    么SpringSecurity框架是如何实现的呢? Spring Security功能的实现主要是由一系列过滤器相互配合完
    成。也称之为过滤器链

    1. org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter

    根据请求封装获取WebAsyncManager,从WebAsyncManager获取/注册的安全上下文可调
    用处理拦截器

    1. org.springframework.security.web.context.SecurityContextPersistenceFilter

    SecurityContextPersistenceFilter主要是使用SecurityContextRepository在session中保存
    或更新一个SecurityContext,并将SecurityContext给以后的过滤器使用,来为后续fifilter
    建立所需的上下文。SecurityContext中存储了当前用户的认证以及权限信息。

    1. org.springframework.security.web.header.HeaderWriterFilter

    向请求的Header中添加相应的信息,可在http标签内部使用security:headers来控制

    1. org.springframework.security.web.csrf.CsrfFilter

    csrf又称跨域请求伪造,SpringSecurity会对所有post请求验证是否包含系统生成的csrf的
    token信息,如果不包含,则报错。起到防止csrf攻击的效果。

    1. org.springframework.security.web.authentication.logout.LogoutFilter

    匹配URL为/logout的请求,实现用户退出,清除认证信息。

    1. org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter

    表单认证操作全靠这个过滤器,默认匹配URL为/login且必须为POST请求。

    1. org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter

    如果没有在配置文件中指定认证页面,则由该过滤器生成一个默认认证页面。

    1. org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter

    由此过滤器可以生产一个默认的退出登录页面

    1. org.springframework.security.web.authentication.www.BasicAuthenticationFilter

    此过滤器会自动解析HTTP请求中头部名字为Authentication,且以Basic开头的头信息。

    1. org.springframework.security.web.savedrequest.RequestCacheAwareFilter

    通过HttpSessionRequestCache内部维护了一个RequestCache,用于缓存
    HttpServletRequest

    1. org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter

    针对ServletRequest进行了一次包装,使得request具有更加丰富的API

    1. org.springframework.security.web.authentication.AnonymousAuthenticationFilter

    当SecurityContextHolder中认证信息为空,则会创建一个匿名用户存入到
    SecurityContextHolder中。spring security为了兼容未登录的访问,也走了一套认证流程,
    只不过是一个匿名的身份。

    1. org.springframework.security.web.session.SessionManagementFilter

    securityContextRepository限制同一用户开启多个会话的数量

    1. org.springframework.security.web.access.ExceptionTranslationFilter

    异常转换过滤器位于整个springSecurityFilterChain的后方,用来转换整个链路中出现的异

    1. org.springframework.security.web.access.intercept.FilterSecurityInterceptor

    获取所配置资源访问的授权信息,根据SecurityContextHolder中存储的用户信息来决定其
    是否有权限。

    表单认证

    3.1 自定义表单登录页

    在config包下编写SecurityConfiguration配置类

    @Configuration
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        
        @Override
        public void configure(WebSecurity web) throws Exception {
            //对静态资源放行
            web.ignoring().antMatchers("/css/**", "/images/**", "/js/**",
                    "/favicon.ico");
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.formLogin()        //开启表单认证
            .loginPage("/toLoginPage")
                    .and().authorizeRequests()
                    .antMatchers("/toLoginPage").permitAll()   //登录请求不需要认证
                    .anyRequest().authenticated();      //所有请求都需要认证
        }
    }
    
    

    访问http://localhost:8080/,会自动跳转自定义的登录页面。

    这时的登录页面的表单的请求参数必须得跟springSecurity默认的一致才行,那么可以自定义吗?答案是肯定的。

    默认值:

    自定义参数代码:

     protected void configure(HttpSecurity http) throws Exception {
            http.formLogin()        //开启表单认证
            .loginPage("/toLoginPage")
                    .loginProcessingUrl("/login")  //登录处理url
                    .usernameParameter("username")  //自定义用户名参数
                    .passwordParameter("password")//自定义密码参数
                    .defaultSuccessUrl("/")   //登录成功跳转路径
                    .and().authorizeRequests()
                    .antMatchers("/toLoginPage").permitAll()   //登录请求不需要认证
                    .anyRequest().authenticated();      //所有请求都需要认证
        http.csrf().disable();
    
        }
    
    3.2 基于数据库实现认证功能

    之前我们所使用的用户名和密码是来源于框架自动生成的, 那么我们如何实现基于数据库中的用户名和
    密码功能呢? 要实现这个得需要实现security的一个UserDetailsService接口, 重写这个接口里面
    loadUserByUsername即可

    1. 编写MyUserDetailsService并实现UserDetailsService接口,重写loadUserByUsername方法
    @Service
    public class MyUserDetailsService implements UserDetailsService {
    
        @Autowired
        private UserService userService;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            User user = userService.findByName(username);
            if(user==null){
                throw new UsernameNotFoundException(username);
            }
    
            // 先声明一个权限集合, 因为构造方法里面不能传入null
            Collection<? extends GrantedAuthority> authorities = new ArrayList<>();
            UserDetails userDetails=new org.springframework.security.core.userdetails.User(username,"{noop}"+user.getPassword()
            ,true,true,true,true,authorities);
            return userDetails;
        }
    }
    

    在上面new UserDetails里面的{noop}代表的是加密方式为:不加密
    2. 在SecurityConfiguration配置类中指定自定义用户认证

    @Configuration
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        @Autowired
        private MyUserDetailsService myUserDetailsService;
    
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(myUserDetailsService);
        }
        
        //省略部分代码...
    }
    
    3.3 数据库用户密码加密认证

    Spring Security 中 PasswordEncoder 就是我们对密码进行编码的工具接口。该接口只有两个功能:
    一个是匹配验证。另一个是密码编码。一般我们常用的算法是BCrypt算法。

    BCrypt算法介绍

    任何应用考虑到安全,绝不能明文的方式保存密码。密码应该通过哈希算法进行加密。 有很
    多标准的算法比如SHA或者MD5,结合salt(盐)是一个不错的选择。 Spring Security 提供了
    BCryptPasswordEncoder类,实现Spring的PasswordEncoder接口使用BCrypt强哈希方法来加密
    密码。BCrypt强哈希方法每次加密的结果都不一样,所以更加的安全。

    bcrypt加密后的字符串形如:

    $2a$10$wouq9P/HNgvYj2jKtUN8rOJJNRVCWvn1XoWy55N3sCkEHZPo3lyWq

    其中$是分割符,无意义;2a是bcrypt加密版本号;10是const的值;而后的前22位是salt值;再
    然后的字符串就是密码的密文了;这里的const值即生成salt的迭代次数,默认值是10,推荐值12。

    1. 修改loadUserByUsername方法里的加密方式
      @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            User user = userService.findByName(username);
            if(user==null){
                throw new UsernameNotFoundException(username);
            }
    
            // 先声明一个权限集合, 因为构造方法里面不能传入null
            Collection<? extends GrantedAuthority> authorities = new ArrayList<>();
            UserDetails userDetails=new org.springframework.security.core.userdetails.User(username,"{bcrypt}"+user.getPassword()
            ,true,true,true,true,authorities);
            return userDetails;
        }
    
    1. 修改数据库里的密码为加密格式

    加密代码如下:

    BCryptPasswordEncoder encoder=new BCryptPasswordEncoder();
    String encode = encoder.encode("123456");
    
    3.4 获取当前登录用户

    在传统web系统中, 我们将登录成功的用户放入session中, 在需要的时候可以从session中获取用户,那么Spring Security中我们如何获取当前已经登录的用户呢?

    方式1:SecurityContextHolder

    UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    

    方式2:Authentication

     @RequestMapping("/getUser2")
        public UserDetails getUser2(Authentication authentication){
            UserDetails principal = (UserDetails) authentication.getPrincipal();
            return principal;
        }
    

    方式3:@AuthenticationPrincipal

    @RequestMapping("/getUser3")
        public UserDetails getUser3(@AuthenticationPrincipal UserDetails userDetails){
            return userDetails;
        }
    
    3.5 remember me功能

    在大多数网站中,都会实现RememberMe这个功能,方便用户在下一次登录时直接登录,避免再次输入用户名以及密码去登录,Spring Security针对这个功能已经帮助我们实现, 下面我们来看下他的原理图.

    简单token方式

    Token=MD5(username+分隔符+expiryTime+分隔符+password)

    注意:这种方式不推荐使用,是将用户密码信息存在前端浏览器的cookie中,不安全。

    实现方式:

    1. 前端页面需要增加remember-me的复选框
    <div class="form-group">
        <div >
          <!--记住我 name为remember-me value值可选true yes 1 on 都行-->
          <input type="checkbox"  name="remember-me" value="true"/>记住我
        </div>
    </div>
    
    1. 后端代码开启remember-me功能
     .and().rememberMe()  //开启remeberMe功能
                    .tokenValiditySeconds(60*60)        //token失效时间
                    .rememberMeParameter("remember-me")     //自定义表单名称
    
    1. 验证,登录成功后查看cookie

    登录成功后,关掉浏览器,再次访问,也不需要登录了。

    持久化的Token生成方式

    token: 随机生成策略,每次访问都会重新生成

    series: 登录序列号,随机生成策略。用户输入用户名和密码登录时,该值重新生成。使用remember-me功能,该值保持不变

    expiryTime: token过期时间。

    CookieValue=encode(series+token)

    1. 后台代码
     @Bean
        public PersistentTokenRepository persistentTokenRepository(){
            JdbcTokenRepositoryImpl tokenRepository=new JdbcTokenRepositoryImpl();
            tokenRepository.setDataSource(dataSource);
    //        启动时创建一张表, 第一次启动的时候创建, 第二次启动的时候需要注释掉, 否则
    //        会报错
            tokenRepository.setCreateTableOnStartup(true);
            return tokenRepository;
        }
    

    注意: tokenRepository.setCreateTableOnStartup(true); 第一次设置为true,后面启动需要设置为false

    1. 配置持久化token
     .and().rememberMe()  //开启remeberMe功能
                    .tokenValiditySeconds(60*60)        //token失效时间
                    .rememberMeParameter("remember-me")     //自定义表单名称
                    .tokenRepository(persistentTokenRepository())  //设置tokenRepository
    
    1. 登录成功,查看数据

    这两种方式都是依赖cookie的,如果cookie被窃取,会有安全问题。不需要登录,将cookie拷贝到postman里面就能够直接调用接口。

    对重要的接口我们需要处理,处理方法如下:

     public String hello(){
            //获取认证信息
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            //判断如果认证信息是来源于remember-me就拦截
            if(RememberMeAuthenticationToken.class.isAssignableFrom(authentication.getClass())){
                throw new RememberMeAuthenticationException("请重写登录");
            }
            return "hello security";
        }
    
    3.6 自定义登录成功或失败

    有些时候登录成功或失败后需要做一些后续操作,比如日志收集,发送请求等。

    自定义成功处理

    实现AuthenticationSuccessHandler接口,并重写onAnthenticationSuccesss()方法

    自定义失败处理

    实现AuthenticationFailureHandler接口,并重写onAuthenticationFailure()方法

    1. 登录处理类
    @Service
    public class LoginHandler implements AuthenticationSuccessHandler,AuthenticationFailureHandler {
    
        private RedirectStrategy redirectStrategy = new
                DefaultRedirectStrategy();
    
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
            System.out.println("登录失败");
        }
    
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            System.out.println("登录成功");
            redirectStrategy.sendRedirect(request,response,"/");
        }
    }
    
    1. 配置登录处理
    .successHandler(loginHandler)
    .failureHandler(loginHandler)
    

    完整配置如下:

    @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.formLogin()        //开启表单认证
            .loginPage("/toLoginPage")
                    .loginProcessingUrl("/login")  //登录处理url
                    .usernameParameter("username")  //自定义用户名参数
                    .passwordParameter("password")//自定义密码参数
                    .defaultSuccessUrl("/")   //登录成功跳转路径
                    .successForwardUrl("/")
                    .successHandler(loginHandler)
                    .failureHandler(loginHandler)
                    .and().rememberMe()  //开启remeberMe功能
                    .tokenValiditySeconds(60*60)        //token失效时间
                    .rememberMeParameter("remember-me")     //自定义表单名称
                    .tokenRepository(persistentTokenRepository())  //设置tokenRepository
                    .and().authorizeRequests()
                    .antMatchers("/toLoginPage").permitAll()   //登录请求不需要认证
                    .anyRequest().authenticated();      //所有请求都需要认证
        http.csrf().disable();
        // 允许iframe加载页面
        http.headers().frameOptions().sameOrigin();
        }
    

    异步登录

    1. 前端页面改造
    <form id="formLogin" action="/login" method="post">
            <div class="panel loginbox">
             .....
              <div style="padding:30px;">
                <input type="button" onclick="login()"
                   class="button button-block bg-main text-
    big input-big" value="登录">
              </div>
            </div>
          </form>
        </div>
      </div>
    </div>
    <script>
      function login() {
        $.ajax({
          type: "POST",//方法类型
          dataType: "json",//服务器预期返回类型
          url: "/login",  // 登录url
          data: $("#formLogin").serialize(),
          success: function (data) {
            console.log(data)
            if (data.code == 200) {
              window.location.href = "/";
           } else {
              alert(data.message);
           }
         }
       });
     }
    </script>
    
    1. 后端改造
      @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            System.out.println("登录成功");
            Map result = new HashMap();
            result.put("code", HttpStatus.OK.value());// 设置响应码
            result.put("message", "登录成功");// 设置响应信息
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(result));
        }
    
    3.7 退出登录

    只需要发送请求,请求路径为/logout即可, 当然这个路径也可以自行在配置类中自行指定, 同时退出
    操作也有对应的自定义处理LogoutSuccessHandler,退出登录成功后执行,退出的同时如果有remember-me的数据,同时一并删除

    1. 前端页面
    <a class="button button-little bg-red" href="/logout">
          <span class="icon-power-off"></span>退出登录</a></div>
    
    1. 后端配置

    实现LogoutSuccessHandler接口

     @Override
        public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            System.out.println("退出登录后续处理");
        }
    
    .and().logout().logoutUrl("/logout")    //设置退出登录url
                    .logoutSuccessHandler(loginHandler)     //自定义退出处理
    

    图形验证码

    1. 验证码图片生成代码
    @RequestMapping("/image/code")
        public void imageCode(HttpServletResponse response) throws IOException {
            ImageCode imageCode = createImageCode();
            BufferedImage image = imageCode.getImage();
            response.setContentType("image/jpeg");
            ImageIO.write(image,"jpeg",response.getOutputStream());
        }
    
        private ImageCode createImageCode() {
            int width = 100;    // 验证码图片宽度
            int height = 36;    // 验证码图片长度
            int length = 4;     // 验证码位数
            //创建一个带缓冲区图像对象
            BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            //获得在图像上绘图的Graphics对象
            Graphics g = image.getGraphics();
    
            Random random = new Random();
    
            //设置颜色、并随机绘制直线
            g.setColor(getRandColor(200, 250));
            g.fillRect(0, 0, width, height);
            g.setFont(new Font("宋体", Font.ITALIC, 20));
            g.setColor(getRandColor(160, 200));
            for (int i = 0; i < 155; i++) {
                int x = random.nextInt(width);
                int y = random.nextInt(height);
                int xl = random.nextInt(12);
                int yl = random.nextInt(12);
                g.drawLine(x, y, x + xl, y + yl);
            }
    
            //生成随机数 并绘制
            StringBuilder sRand = new StringBuilder();
            for (int i = 0; i < length; i++) {
                String rand = String.valueOf(random.nextInt(10));
                sRand.append(rand);
                g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
                g.drawString(rand, 13 * i + 6, 16);
            }
            g.dispose();
            return new ImageCode(image, sRand.toString());
        }
    
        private Color getRandColor(int fc, int bc) {
            Random random = new Random();
            if (fc > 255) {
                fc = 255;
            }
            if (bc > 255) {
                bc = 255;
            }
            int r = fc + random.nextInt(bc - fc);
            int g = fc + random.nextInt(bc - fc);
            int b = fc + random.nextInt(bc - fc);
            return new Color(r, g, b);
        }
    
    1. 校验验证码是否正确

    Spring Security的认证校验是由UsernamePasswordAuthenticationFilter过滤器完成的,所以我们的验证码校验逻辑应该在这个过滤器之前。验证码通过后才能到后续的操作.

    实现OncePerRequestFilter接口,写过滤其代码校验验证码。代码略

    1. 配置过滤器

    将我们的过滤器配置在UsernamePassword校验前

     http.addFilterBefore(validateCodeFilter,
    UsernamePasswordAuthenticationFilter.class);
    

    Session管理

    5.1 会话超时
    1. 配置session会话超时时间,默认30分钟
    server.servlet.session.timeout=60s
    

    注意:设置低于1分钟不起效

    1. 自定义设置session超时后跳转地址
    http.sessionManagement() //设置session管理
            .invalidSessionUrl("/toLoginPage");
    
    5.2 并发控制

    并发控制即同一个账号同时在线个数,同一个账号同时在线个数如果设置为1表示,该账号在同一时间内只能有一个有效的登录,如果同一个账号又在其它地方登录,那么就将上次登录的会话过期,即后面的登录会踢掉前面的登录

     http.sessionManagement() //设置session管理
            .invalidSessionUrl("/toLoginPage")             //session无效后跳转路径
            .maximumSessions(1)  //设置session最大会话数量,1代表同一时间只能一个用户登录
            .expiredUrl("/toLoginPage");        //session过期后跳转路径
    

    阻止用户二次登录

    sessionManagement也可以配置 maxSessionsPreventsLogin的值,当达到maximumSessions设置的最大会话个数时阻止登录。

     http.sessionManagement() //设置session管理
            .invalidSessionUrl("/toLoginPage")             //session无效后跳转路径
            .maximumSessions(1)  //设置session最大会话数量,1代表同一时间只能一个用户登录
            .maxSessionsPreventsLogin(true)    //达到最大会话时阻止登录
            .expiredUrl("/toLoginPage");        //session过期后跳转路径
    
    5.3 集群session

    问题描述:
    实际场景中一个服务会至少有两台服务器在提供服务,在服务器前面会有一个nginx做负载均衡,
    用户访问nginx,nginx再决定去访问哪一台服务器。当一台服务宕机了之后,另一台服务器也可以继续
    提供服务,保证服务不中断。如果我们将session保存在Web容器(比如tomcat)中,如果一个用户第一
    次访问被分配到服务器1上面需要登录,当某些访问突然被分配到服务器二上,因为服务器二上没有用
    户在服务器一上登录的会话session信息,服务器二还会再次让用户登录,用户已经登录了还让登录就
    感觉不正常了。

    解决这个问题有一个方案是将session共享,可以存在单独的地方(redis、数据库、mongodb等)。

    1. 引入依赖
    <!-- 基于redis实现session共享 -->
    <dependency>
      <groupId>org.springframework.session</groupId>
      <artifactId>spring-session-data-redis</artifactId>
    </dependency>
    
    1. 设置session存储类型
    #使用redis共享session
    spring.session.store-type=redis
    

    csrf防护机制

    什么是csrf?

    CSRF(Cross-site request forgery),中文名称:跨站请求伪造

    从上图可以看出,要完成一次CSRF攻击,受害者必须依次完成三个步骤:

    1. 登录一个受信任网站A,并生成本地Cookie
    2. 在不登出A的情况下,访问危险网站B
    3. 网站B是个黑客做的网站,他里面有些链接和按钮会让用户调用A网站的接口去做一些对你不利的操作。

    csrf的防御策略

    1. 验证http referer

    接收到请求的时候判断是不是由自己网站发来的
    2. 在请求地址中添加tokon并验证

    黑客之所以能完全伪造用户的请求,是因为验证用户的信息放在cookie里的,所以他可以利用你的cookie来通过安全校验。要抵御csrf,只要在请求里加入黑客不能伪造的信息就可以了。

    比如在请求参数中加入一个token,并且在服务端建一个拦截器来校验这个token。这个token是服务器给前端的,黑客在别的网站上拿不到,这样就可以避免csrf攻击了。
    3. 在http头中自定义属性并验证

    这种方法也是使用token来验证,和上一种方法不同的是是将token放在http头中。

    security防御csrf

    Security依靠org.springframework.security.web.csrf.CsrfFilter拦截器来进行验证token。

    1. 页面发请求时增加token值
     <div>
        <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
    </div>
    
    1. 后端(默认是开启防护的,如某接口不需要防护可以配置)
    //哪些接口不做csrf防护
    http.csrf().ignoringAntMatchers("/user/save");
    

    跨域

    跨域,实际上是浏览器的一种保护处理,如果产生了跨域,服务器在返回结果时就会被浏览器拦截。

    两个网站端口不同,域名不同,协议不同都会被认为是跨域。

    解决跨域

    1. JSONP

    浏览器允许一些带src属性的标签跨域,也就是在某些标签的src属性上写url地址是不会产生跨
    域问题
    2. CORS解决跨域

    CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。浏览器在发起真正的请求之前,会发起一个OPTIONS类型的预检请求,用于请求服务器是否允许跨域,在得到许可的情况下才会发起请求。

    Security处理跨域

    1. 配置cors
    public CorsConfigurationSource corsConfigurationSource() {
            CorsConfiguration corsConfiguration = new CorsConfiguration();
            // 设置允许跨域的站点
            corsConfiguration.addAllowedOrigin("*");
            // 设置允许跨域的http方法
            corsConfiguration.addAllowedMethod("*");
            // 设置允许跨域的请求头
            corsConfiguration.addAllowedHeader("*");
            // 允许带凭证
            corsConfiguration.setAllowCredentials(true);
    
            // 对所有的url生效
            UrlBasedCorsConfigurationSource source = new
                            UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/**", corsConfiguration);
            return source;
    }
    
    http.cors().configurationSource(corsConfigurationSource());
    

    再发请求,成功了

    书山有路勤为径,学海无涯苦作舟
  • 相关阅读:
    【嵌入式linux】(第三步):安装串口终端 (ubuntu安装minicom串口终端)
    vim常用命令总结
    usb调试
    查找当前文件夹内容
    Android SDK开发包国内下载地址
    ubuntu下svn使用指南
    Ubuntu vim显示行号语法高亮自动缩进
    android_handler(一)
    HDU 1241 Oil Deposits (DFS)
    计划
  • 原文地址:https://www.cnblogs.com/javammc/p/15673869.html
Copyright © 2011-2022 走看看