这篇文章主要介绍“springSecurity如何实现前后端分离”,在日常操作中,相信很多人在SpringSecurity如何实现前后端分离问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”SpringSecur
这篇文章主要介绍“springSecurity如何实现前后端分离”,在日常操作中,相信很多人在SpringSecurity如何实现前后端分离问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”SpringSecurity如何实现前后端分离”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
前后端分离模式是指由前端控制页面路由,后端接口也不再返回html数据,而是直接返回业务数据,数据一般是JSON格式。Spring Security默认的表单登录方式,在未登录或登录成功时会发起页面重定向,在提交登录数据时,也不是jsON格式。要支持前后端分离模式,要对这些问题进行改造。
Spring Security默认提供账号密码认证方式,具体实现是在UsernamePassWordAuthenticationFilter 中。因为是表单提交,所以Filter中用request.getParameter(this.usernameParameter) 来获取用户信息。当我们将数据改成JSON,并放入Http Body后,getParameter 就没法获取到信息。
要解决这个问题,就要新建Filter来替换UsernamePasswordAuthenticationFilter ,然后覆盖掉获取用户的方法。
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())); }}
注册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);} @Overrideprotected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {return new AntPathRequestMatcher(loginProcessingUrl, "POST");}}
这一步比较简单,直接关闭表单登录,然后注册我们自己的Filter。
http .fORMLogin().disable() .apply(new JsonUsernamePasswordLoginConfigurer<>())
经过这三步,Spring Security就能识别JSON格式的用户信息。
有几个场景会触发Spring Security的重定向:
未登录,重定向到登录页面
登录验证成功,重定向到默认页面
退出登录,重定向到默认页面
我们要对这几个场景分别处理,给前端返回错误信息,而不是重定向。
未登录的请求会被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();}))
登录成功或失败后的行为由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(); }));
// 退出登录.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(); }))
由于前端直接调用登录接口,跳过了获取登录页面的步骤,所以服务端没有机会将CSRF Token传给前段,所以要把POST /login接口的CSRF校验剔除掉。
http.csrf(it -> it.ignoringRequestMatchers("/login", "POST"))
到此,关于“SpringSecurity如何实现前后端分离”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!
--结束END--
本文标题: SpringSecurity如何实现前后端分离
本文链接: https://www.lsjlt.com/news/351575.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-05-16
2024-05-16
2024-05-16
2024-05-16
2024-05-16
2024-05-16
2024-05-16
2024-05-16
2024-05-16
2024-05-16
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0