中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SpringSecurity如何實現前后端分離

發布時間:2023-03-14 16:55:08 來源:億速云 閱讀:151 作者:iii 欄目:開發技術

這篇文章主要介紹“SpringSecurity如何實現前后端分離”,在日常操作中,相信很多人在SpringSecurity如何實現前后端分離問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”SpringSecurity如何實現前后端分離”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

    前后端分離模式是指由前端控制頁面路由,后端接口也不再返回html數據,而是直接返回業務數據,數據一般是JSON格式。Spring Security默認的表單登錄方式,在未登錄或登錄成功時會發起頁面重定向,在提交登錄數據時,也不是JSON格式。要支持前后端分離模式,要對這些問題進行改造。

    1. 認證信息改成JSON格式

    Spring Security默認提供賬號密碼認證方式,具體實現是在UsernamePasswordAuthenticationFilter 中。因為是表單提交,所以Filter中用request.getParameter(this.usernameParameter) 來獲取用戶信息。當我們將數據改成JSON,并放入HTTP Body后,getParameter 就沒法獲取到信息。

    要解決這個問題,就要新建Filter來替換UsernamePasswordAuthenticationFilter ,然后覆蓋掉獲取用戶的方法。

    1.1 新建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 新建JsonUsernamePasswordLoginConfigurer

    注冊Filter有兩種方式,一給是直接調用httpSecurity的addFilterAt(Filter filter, Class<? extends Filter> atFilter) ,另一個是注冊通過AbstractHttpConfigurer 來注冊。我們選擇第二種方式來注冊Filter,因為AbstractHttpConfigurer 在初始化 UsernamePasswordAuthenticationFilter 的時候,會額外設置一些信息。新建一個自己的AbstractHttpConfigurer

    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);
    	}
     
    	@Override
    	protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
    		return new AntPathRequestMatcher(loginProcessingUrl, "POST");
    	}
    }

    1.3 注冊JJsonUsernamePasswordLoginConfigurer到HttpSecurity

    這一步比較簡單,直接關閉表單登錄,然后注冊我們自己的Filter。

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

    經過這三步,Spring Security就能識別JSON格式的用戶信息。

    2. 去掉重定向

    有幾個場景會觸發Spring Security的重定向:

    • 未登錄,重定向到登錄頁面

    • 登錄驗證成功,重定向到默認頁面

    • 退出登錄,重定向到默認頁面

    我們要對這幾個場景分別處理,給前端返回錯誤信息,而不是重定向。

    2.1 未登錄請求

    未登錄的請求會被AuthorizationFilter攔截,并拋出異常。異常被AuthenticationEntryPoint處理,默認會觸發重定向到登錄頁。我們通過自定義AuthenticationEntryPoint來取消重定向行為,改為返回JSON信息。

    http
    // 1. 未登錄的請求會被AuthorizationFilter攔截,并拋出異常。
    .exceptionHandling(it -> it.authenticationEntryPoint((request, response, authException) -> {
        log.info("get exception {}", authException.getClass());
        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 來控制。由于上面我們自定義了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 退出登錄

    // 退出登錄
    .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校驗

    由于前端直接調用登錄接口,跳過了獲取登錄頁面的步驟,所以服務端沒有機會將CSRF Token傳給前段,所以要把POST /login接口的CSRF校驗剔除掉。

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

    到此,關于“SpringSecurity如何實現前后端分離”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    若羌县| 涿鹿县| 东源县| 延边| 嫩江县| 乐陵市| 甘德县| 乌拉特前旗| 望城县| 安达市| 新河县| 化州市| 定南县| 瑞丽市| 武宣县| 秦皇岛市| 西昌市| 万荣县| 汝城县| 青海省| 苍山县| 江华| 安塞县| 玉门市| 莱西市| 罗田县| 安康市| 宁南县| 红河县| 北流市| 沂源县| 安泽县| 靖江市| 庆安县| 沁源县| 石棉县| 凤凰县| 万荣县| 丹寨县| 奉化市| 桑日县|