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());

再發請求,成功了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章