Spring security如何重寫(xiě)Filter實(shí)現(xiàn)json登錄
Spring security 重寫(xiě)Filter實(shí)現(xiàn)json登錄
在使用SpringSecurity中,大伙都知道默認(rèn)的登錄數(shù)據(jù)是通過(guò)key/value的形式來(lái)傳遞的,默認(rèn)情況下不支持JSON格式的登錄數(shù)據(jù),如果有這種需求,就需要自己來(lái)解決,本文主要解決此問(wèn)題:
JSON登錄
上面演示的是一種原始的登錄方案,如果想將用戶(hù)名密碼通過(guò)JSON的方式進(jìn)行傳遞,則需要自定義相關(guān)過(guò)濾器,通過(guò)分析源碼我們發(fā)現(xiàn),默認(rèn)的用戶(hù)名密碼提取在UsernamePasswordAuthenticationFilter過(guò)濾器中,部分源碼如下:
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
private boolean postOnly = true;
public UsernamePasswordAuthenticationFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); // Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(passwordParameter);
}
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(usernameParameter);
}
//...
//...
}
從這里可以看到,默認(rèn)的用戶(hù)名/密碼提取就是通過(guò)request中的getParameter來(lái)提取的,如果想使用JSON傳遞用戶(hù)名密碼,只需要將這個(gè)過(guò)濾器替換掉即可,自定義過(guò)濾器如下:
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE) || request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
ObjectMapper mapper = new ObjectMapper();
UsernamePasswordAuthenticationToken authRequest = null;
try (InputStream is = request.getInputStream()) {
Map<String, String> authenticationBean = mapper.readValue(is, Map.class);
authRequest = new UsernamePasswordAuthenticationToken(authenticationBean.get("username"), authenticationBean.get("password"));
} catch (IOException e) {
e.printStackTrace();
authRequest = new UsernamePasswordAuthenticationToken("", "");
} finally {
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
} else {
return super.attemptAuthentication(request, response);
}
}
}
這里只是將用戶(hù)名/密碼的獲取方案重新修正下,改為了從JSON中獲取用戶(hù)名密碼,然后在SecurityConfig中作出如下修改:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().csrf().disable();
http.addFilterAt(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
filter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
RespBean respBean = RespBean.ok("登錄成功!");
out.write(new ObjectMapper().writeValueAsString(respBean));
out.flush();
out.close();
}
});
filter.setAuthenticationFailureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
resp.setContentype("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
RespBean respBean = RespBean.error("登錄失敗!");
out.write(new ObjectMapper().writeValueAsString(respBean));
out.flush();
out.close();
}
});
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}
搞定~
Spring security5 使用json登錄
public class CustomUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
@SneakyThrows(IOException.class) //lombok try catch
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (request.getContentType().contains(MediaType.APPLICATION_JSON_VALUE)) {
ObjectMapper mapper = new ObjectMapper();
Map<String,String> map = mapper.readValue(request.getInputStream(), Map.class);
String username = map.get(super.getUsernameParameter());
String password = map.get(super.getPasswordParameter());
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
return super.attemptAuthentication(request, response);
}
}
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAt(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
}
CustomUsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() throws Exception {
CustomUsernamePasswordAuthenticationFilter filter = new CustomUsernamePasswordAuthenticationFilter();
filter.setAuthenticationManager(super.authenticationManagerBean());
filter.setFilterProcessesUrl(customSecurityProperties.getLoginUrl());
//處理登錄成功
filter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler());
//處理登錄失敗
filter.setAuthenticationFailureHandler(new AuthenticationFailureHandler());
return filter;
}
}
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java游戲服務(wù)器系列之Netty相關(guān)知識(shí)總結(jié)
今天帶大家來(lái)學(xué)習(xí)Java游戲服務(wù)器的相關(guān)知識(shí),文中對(duì)Netty作了非常詳細(xì)的介紹,對(duì)正在學(xué)習(xí)java的小伙伴們有很好的幫助,需要的朋友可以參考下2021-05-05
IDEA中springboot的熱加載thymeleaf靜態(tài)html頁(yè)面的方法
這篇文章主要介紹了IDEA中springboot的熱加載thymeleaf靜態(tài)html頁(yè)面的方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07
SpringBoot?全局線(xiàn)程池配置及應(yīng)用小結(jié)
為了提高應(yīng)用程序的性能和響應(yīng)速度,線(xiàn)程池是一個(gè)非常重要的工具,本文主要介紹了Spring?Boot?全局線(xiàn)程池配置及應(yīng)用,具有一定的參考價(jià)值,感興趣的可以了解一下2024-05-05
java中的final關(guān)鍵字詳解及實(shí)例
這篇文章主要介紹了 java中的final關(guān)鍵字詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-03-03
Java中值類(lèi)型和引用類(lèi)型的比較與問(wèn)題解決
這篇文章主要給大家介紹了關(guān)于Java中值類(lèi)型和引用類(lèi)型的比較與問(wèn)題解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12
Java并發(fā)教程之volatile關(guān)鍵字詳解
這篇文章主要給大家介紹了關(guān)于Java并發(fā)教程之volatile關(guān)鍵字的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11

