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

SpringSecurity實(shí)現(xiàn)前后端分離的示例詳解

 更新時(shí)間:2023年03月14日 10:01:33   作者:Singlerr  
Spring Security默認(rèn)提供賬號(hào)密碼認(rèn)證方式,具體實(shí)現(xiàn)是在UsernamePasswordAuthenticationFilter 中,這篇文章主要介紹了SpringSecurity實(shí)現(xiàn)前后端分離的示例詳解,需要的朋友可以參考下

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

1. 認(rèn)證信息改成JSON格式

Spring Security默認(rèn)提供賬號(hào)密碼認(rèn)證方式,具體實(shí)現(xiàn)是在UsernamePasswordAuthenticationFilter 中。因?yàn)槭潜韱翁峤?,所以Filter中用request.getParameter(this.usernameParameter) 來獲取用戶信息。當(dāng)我們將數(shù)據(jù)改成JSON,并放入HTTP Body后,getParameter 就沒法獲取到信息。

要解決這個(gè)問題,就要新建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

注冊(cè)Filter有兩種方式,一給是直接調(diào)用httpSecurity的addFilterAt(Filter filter, Class<? extends Filter> atFilter) ,另一個(gè)是注冊(cè)通過AbstractHttpConfigurer 來注冊(cè)。我們選擇第二種方式來注冊(cè)Filter,因?yàn)锳bstractHttpConfigurer 在初始化 UsernamePasswordAuthenticationFilter 的時(shí)候,會(huì)額外設(shè)置一些信息。新建一個(gè)自己的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 注冊(cè)JJsonUsernamePasswordLoginConfigurer到HttpSecurity

這一步比較簡單,直接關(guān)閉表單登錄,然后注冊(cè)我們自己的Filter。

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

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

2. 去掉重定向

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

  • 未登錄,重定向到登錄頁面
  • 登錄驗(yàn)證成功,重定向到默認(rèn)頁面
  • 退出登錄,重定向到默認(rèn)頁面

我們要對(duì)這幾個(gè)場景分別處理,給前端返回錯(cuò)誤信息,而不是重定向。

2.1 未登錄請(qǐng)求

未登錄的請(qǐng)求會(huì)被AuthorizationFilter攔截,并拋出異常。異常被AuthenticationEntryPoint處理,默認(rèn)會(huì)觸發(fā)重定向到登錄頁。我們通過自定義AuthenticationEntryPoint來取消重定向行為,改為返回JSON信息。

http
// 1. 未登錄的請(qǐng)求會(huì)被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\\": \\"用戶名密碼錯(cuò)誤\\"}";
								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校驗(yàn)

由于前端直接調(diào)用登錄接口,跳過了獲取登錄頁面的步驟,所以服務(wù)端沒有機(jī)會(huì)將CSRF Token傳給前段,所以要把POST /login接口的CSRF校驗(yàn)剔除掉。

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

到此這篇關(guān)于SpringSecurity實(shí)現(xiàn)前后端分離的示例詳解的文章就介紹到這了,更多相關(guān)SpringSecurity前后端分離內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

阳泉市| 子长县| 台北市| 武汉市| 武陟县| 新河县| 平山县| 香格里拉县| 鲁甸县| 岳普湖县| 泗洪县| 扬中市| 磴口县| 盖州市| 东乡| 正宁县| 南澳县| 台安县| 西盟| 罗平县| 化州市| 突泉县| 耒阳市| 尚义县| 会理县| 包头市| 宜良县| 广饶县| 西安市| 宜城市| 富蕴县| 尤溪县| 江华| 崇义县| 称多县| 南岸区| 晋城| 随州市| 康马县| 西青区| 南乐县|