最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解SpringSecurity如何實現(xiàn)前后端分離

 更新時間:2023年03月30日 10:53:36   作者:DigitalDreamer  
這篇文章主要為大家介紹了詳解SpringSecurity如何實現(xiàn)前后端分離,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

Spring Security存在的問題

前后端分離模式是指由前端控制頁面路由,后端接口也不再返回html數(shù)據(jù),而是直接返回業(yè)務(wù)數(shù)據(jù),數(shù)據(jù)一般是JSON格式。

Spring Security默認支持的表單認證方式,會存在兩個問題:

  • 表單的HTTP Content-Type是application/x-www-form-urlencoded,不是JSON格式。
  • Spring Security會在用戶未登錄或登錄成功時會發(fā)起頁面重定向,重定向到登錄頁或登錄成功頁面。

要支持前后端分離的模式,我們要對這些問題進行改造。

改造Spring Security的認證方式

1. 登錄請求改成JSON方式

Spring Security默認提供賬號密碼認證方式,具體實現(xiàn)是在UsernamePasswordAuthenticationFilter類中。因為是表單提交,所以Filter中用request.getParameter(this.usernameParameter) 來獲取用戶賬號和密碼信息。當我們將請求類型改成application/json后,getParameter方法就獲取不到信息。

要解決這個問題,就要新建一個Filter來替換UsernamePasswordAuthenticationFilter ,然后重新實現(xiàn)獲取用戶的方法。

1.1 新建JSON版Filter - JsonUsernamePasswordAuthenticationFilter

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import org.springframework.data.util.Pair;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
public class JsonUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
        if (!request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }
        Pair<String, String> usernameAndPassword = obtainUsernameAndPassword(request);
        String username = usernameAndPassword.getFirst();
        username = (username != null) ? username.trim() : "";
        String password = usernameAndPassword.getSecond();
        password = (password != null) ? password : "";
        UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
                password);
        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);
    }
    @SneakyThrows
    private Pair<String, String> obtainUsernameAndPassword(HttpServletRequest request) {
        JSONObject map = JSON.parseObject(request.getInputStream(), JSONObject.class);
        return Pair.of(map.getString(getUsernameParameter()), map.getString(getPasswordParameter()));
    }
}

1.2 新建Configurer來注冊Filter - JsonUsernamePasswordLoginConfigurer

注冊Filter有兩種方式,一給是直接調(diào)用httpSecurity的addFilterAt(Filter filter, Class<? extends Filter> atFilter) ,另一個是通過AbstractHttpConfigurer 來注冊。因為我們繼承了原來的賬密認證方式,考慮到兼容原有邏輯,我們選擇Spring Security默認的Configurer注冊方式來注冊Filter。AbstractHttpConfigurer 在初始化 UsernamePasswordAuthenticationFilter 的時候,會額外設(shè)置一些信息。

新建一個JsonUsernamePasswordLoginConfigurer直接繼承AbstractAuthenticationFilterConfigurer。

import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
public final class JsonUsernamePasswordLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
        AbstractAuthenticationFilterConfigurer<H, JsonUsernamePasswordLoginConfigurer<H>, JsonUsernamePasswordAuthenticationFilter> {
	public JsonUsernamePasswordLoginConfigurer() {
		super(new JsonUsernamePasswordAuthenticationFilter(), null);
	}
        // 去掉登錄處理接口的權(quán)限校驗
	@Override
	protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
		return new AntPathRequestMatcher(loginProcessingUrl, "POST");
	}
}

1.3 將自定義Configurer注冊到HttpSecurity上

這一步比較簡單,我們先關(guān)閉原來的表單認證,然后注冊我們自己的Configurer,實現(xiàn)JSON版認證方式。

http
    .formLogin().disable()
    .apply(new JsonUsernamePasswordLoginConfigurer<>())

經(jīng)過這三步,Spring Security就能識別JSON格式的用戶信息了。

2. 關(guān)閉頁面重定向

有幾個場景會觸發(fā)Spring Security的重定向:

  • 當前用戶未登錄,重定向到登錄頁面
  • 登錄驗證成功,重定向到默認頁面
  • 退出登錄成功,重定向到默認頁面

我們要對這幾個場景分別處理,給前端返回JSON格式的描述信息,而不是發(fā)起重定向。

2.1 當前用戶未登錄

用戶發(fā)起未登錄的請求會被AuthorizationFilter攔截,并拋出AccessDeniedException異常。異常被AuthenticationEntryPoint處理,默認會觸發(fā)重定向到登錄頁。Spring Security開放了配置,允許我們自定義AuthenticationEntryPoint。那么我們就通過自定義AuthenticationEntryPoint來取消重定向行為,將接口改為返回JSON信息。

http.exceptionHandling(it -> it.authenticationEntryPoint((request, response, authException) -> {
        String msg = "{\\"msg\\": \\"用戶未登錄\\"}";
        response.setStatus(HttpStatus.FORBIDDEN.value());
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        PrintWriter writer = response.getWriter();
        writer.write(msg);
        writer.flush();
        writer.close();
    }))

2.2 登錄成功/失敗

登錄成功或失敗后的行為由AuthenticationSuccessHandler 和AuthenticationFailureHandler 來控制。原來是在**formLogin(it->it.successHandler(null))**里配置它們,由于上面我們自定義了JsonUsernamePasswordLoginConfigurer ,所以要在我們自己的Configurer 上配置AuthenticationSuccessHandler 和AuthenticationFailureHandler 。

http
    .formLogin().disable()
    .apply((SecurityConfigurerAdapter) new JsonUsernamePasswordLoginConfigurer<>()
            .successHandler((request, response, authentication) -> {
		String msg = "{\\"msg\\": \\"登錄成功\\"}";
		response.setStatus(HttpStatus.OK.value());
		response.setContentType(MediaType.APPLICATION_JSON_VALUE);
		PrintWriter writer = response.getWriter();
		writer.write(msg);
		writer.flush();
		writer.close();
            })
            .failureHandler((request, response, exception) -> {
                String msg = "{\\"msg\\": \\"用戶名密碼錯誤\\"}";
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
		response.setContentType(MediaType.APPLICATION_JSON_VALUE);
		PrintWriter writer = response.getWriter();
		writer.write(msg);
		writer.flush();
		writer.close();
            }));

2.3 退出登錄

退出登錄是在LogoutConfigurer配置,退出成功后,會觸發(fā)LogoutSuccessHandler操作,我們也重寫它的處理邏輯。

http.logout(it -> it
        .logoutSuccessHandler((request, response, authentication) -> {
            String msg = "{\\"msg\\": \\"退出成功\\"}";
            response.setStatus(HttpStatus.OK.value());
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            PrintWriter writer = response.getWriter();
            writer.write(msg);
            writer.flush();
            writer.close();
        }))

3. 最后處理CSRF校驗

前后端分離后,如果頁面是放在CDN上,那么前段直至發(fā)起登錄請求之前,都沒機會從后端拿到CSRF Token。所以,登錄請求會被Spring Security的CsrfFilter攔截。

要避免這種情況,一種方式是發(fā)起登錄請求前,先調(diào)用接口獲取CSRF Token;另一種方式是先關(guān)閉登錄接口的CSRF校驗。方式二配置如下:

http.csrf(it -> it.ignoringRequestMatchers("/login", "POST"))

總結(jié)

至此,前后端分離的基本工作就完成了。在實踐的過程中必然還有其他問題,歡迎大家一起交流探討。

以上就是詳解SpringSecurity如何實現(xiàn)前后端分離的詳細內(nèi)容,更多關(guān)于SpringSecurity前后端分離的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java jdbc連接和使用詳細介紹

    java jdbc連接和使用詳細介紹

    這篇文章主要介紹了 java jdbc連接和使用詳細介紹的相關(guān)資料,需要的朋友可以參考下
    2016-12-12
  • Nacos簡介最新收藏版

    Nacos簡介最新收藏版

    Nacos?是阿里巴巴推出來的一個新開源項目,這是一個更易于構(gòu)建云原生應(yīng)用的動態(tài)服務(wù)發(fā)現(xiàn)、配置管理和服務(wù)管理平臺,Nacos?幫助您更敏捷和容易地構(gòu)建、交付和管理微服務(wù)平臺,對Nacos相關(guān)基本知識感興趣的朋友一起看看吧
    2023-08-08
  • 詳解Java注解實現(xiàn)自己的ORM

    詳解Java注解實現(xiàn)自己的ORM

    這篇文章主要介紹了Java注解實現(xiàn)自己的ORM知識,本文通過示例代碼給大家講解的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2022-10-10
  • SpringMvc3+extjs4實現(xiàn)上傳與下載功能

    SpringMvc3+extjs4實現(xiàn)上傳與下載功能

    這篇文章主要為大家詳細介紹了SpringMvc3+extjs4實現(xiàn)上傳與下載功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • Java獲得指定區(qū)間數(shù)的方法

    Java獲得指定區(qū)間數(shù)的方法

    這篇文章主要介紹了Java獲得指定區(qū)間數(shù)的方法,涉及java數(shù)值運算的相關(guān)技巧,需要的朋友可以參考下
    2015-05-05
  • InterProcessMutex實現(xiàn)zookeeper分布式鎖原理

    InterProcessMutex實現(xiàn)zookeeper分布式鎖原理

    本文主要介紹了InterProcessMutex實現(xiàn)zookeeper分布式鎖原理,文中根據(jù)實例編碼詳細介紹的十分詳盡,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • java.lang.NumberFormatException異常解決方案詳解

    java.lang.NumberFormatException異常解決方案詳解

    這篇文章主要介紹了java.lang.NumberFormatException異常解決方案詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • java?啟動參數(shù)?springboot?idea詳解

    java?啟動參數(shù)?springboot?idea詳解

    這篇文章主要介紹了java?啟動參數(shù)?springboot?idea的相關(guān)知識,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-09-09
  • JavaWeb開發(fā)實現(xiàn)備忘錄

    JavaWeb開發(fā)實現(xiàn)備忘錄

    這篇文章主要為大家詳細介紹了JavaWeb開發(fā)實現(xiàn)備忘錄,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • 基于SSM框架之個人相冊示例代碼

    基于SSM框架之個人相冊示例代碼

    本篇文章主要介紹了基于SSM框架之個人相冊示例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-03-03

最新評論

北川| 绍兴县| 马山县| 忻州市| 彰武县| 遂川县| 勐海县| 正定县| 朝阳县| 星子县| 潜山县| 卓资县| 玉龙| 游戏| 常熟市| 白玉县| 池州市| 定边县| 永康市| 友谊县| 罗山县| 县级市| 启东市| 武鸣县| 英德市| 广汉市| 班戈县| 正定县| 剑阁县| 夏邑县| 集贤县| 灵武市| 江门市| 大田县| 克东县| 孝义市| 琼结县| 玉屏| 运城市| 明光市| 会泽县|