一、自定義過濾器
在【深入淺出Spring Security(二)】Spring Security的實現(xiàn)原理 中小編闡述了默認加載的過濾器,里面有些過濾器有時并不能滿足開發(fā)中的實際需求,這個時候就需要我們自定義過濾器,然后填入或者替換掉原先存在的過濾器。
首先闡述一下添加過濾器的四個方法(都是HttpSecurity
中的),下面方法第一個參數(shù)都是要加入的過濾器,第二個參數(shù)是針對已存在過濾器的Class對象:
-
addFilterAt(filter,atFilter)
:這個相當于線性表的插入操作,把 filter 插入在 atFilter 的位置上。 -
addFilterBefore(filter,atFilter)
:這個是在 atFilter 前插入一個 filter 過濾器; -
addFilterAfter(filter,atFilter)
:這個顧名思義是在 atFilter 后插入一個 filter 過濾器;
自定義登錄認證過濾器
在 【深入淺出Spring Security(三)】默認登錄認證的實現(xiàn)原理 中小編述說過默認的用戶登錄認證是走的 UsernamePasswordAuthenticationFilter
,它是通過表單數(shù)據(jù)傳輸?shù)姆绞?,然后通過 request.getParameter(username/password)
的方式獲取用戶名密碼進行認證的。但在前后端分離中,當提交登錄數(shù)據(jù)給后端是 JSON 格式的話,咱就要自定義過濾器去處理這個數(shù)據(jù)獲取用戶名和密碼進行認證了。
既然是換了一種登錄認證過濾器,而且用于前后端分離,咱就可以把一些不必要的默認過濾器給pass掉。當我們調(diào)用 HttpSecurity 中的 formLogin() 方法的時候,會幫我們加載如下圈到的三個過濾器。
- UsernamePasswordAuthenticationFilter:負責表單認證的;
- DefaultLoginPageGeneratingFilter:提供登錄界面的;
- DefaultLogoutPageGeneratingFilter:提供注銷界面的;
那當我們在自定義 SecurityFilterChain 的時候不去調(diào)用 formLogin 方法的話,那這三個過濾器就相當于被我們 pass 掉了。
自定義 LoginFilter
既然咱要去實現(xiàn)一個 JSON 數(shù)據(jù)格式的認證,首先得先對請求進行一些判斷:
- 請求方式應(yīng)該是 POST 方式;
- 請求發(fā)來的數(shù)據(jù)應(yīng)該是 JSON 格式的。
不滿足這些的話,應(yīng)該去采取默認配置的登錄認證。
實現(xiàn)方式很簡單:
咱去繼承 UsernamePasswordAuthenticationFilter
重寫 attemptAuthentication 方法,實現(xiàn)自己的認證方式即可。
代碼如下:
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
public LoginFilter() {
}
public LoginFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
// 判斷請求方式是否是 POST 方式
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
// 然后判斷是否是 JSON 格式的數(shù)據(jù)
if(request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)) {
// 如果是的話就從 JSON 中取出用戶信息進行認證
try {
Map<String,String> userInfo = JSONObject.parseObject(request.getInputStream(), Map.class);
String username = userInfo.get(getUsernameParameter());
String password = userInfo.get(getPasswordParameter());
// 封裝成Authentication
UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
password);
// 仿造父類去調(diào)用AuthenticationManager.authenticate認證就可以了
setDetails(request,authRequest);
return getAuthenticationManager().authenticate(authRequest);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// 否則用父類的方式去認證
return super.attemptAuthentication(request, response);
}
}
配置 LoginFilter
這里使用的新版本的注入方式,而不是使用繼承 WebSecurityConfigurerAdapter
的方式,因為 WebSecurityConfigurerAdapter 已經(jīng)被棄用了,咱還是與時俱進吧。文章來源:http://www.zghlxwxcb.cn/news/detail-475301.html
@EnableWebSecurity
public class SecurityConfig {
/**
* 構(gòu)造基于內(nèi)存的數(shù)據(jù)源管理
* @return InMemoryUserDetailsManager
*/
@Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager(){
return new InMemoryUserDetailsManager(User
.withUsername("admin")
.password("{noop}123")
.authorities("admin")
.build()
);
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.logout()
.logoutSuccessHandler(this::onAuthenticationSuccess)
.logoutUrl("/api/auth/logout")
.and()
.addFilterAt(loginFilter(http), UsernamePasswordAuthenticationFilter.class) // 注意配置 filter 哦
.csrf()
.disable()
.build();
}
/**
* 自定義 AuthenticationManager
* @param http
* @return AuthenticationManager
* @throws Exception
*/
@Bean
public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
return http.
getSharedObject(AuthenticationManagerBuilder.class)
.userDetailsService(inMemoryUserDetailsManager())
.and()
.build();
}
@Bean
public LoginFilter loginFilter(HttpSecurity http) throws Exception {
LoginFilter loginFilter = new LoginFilter(authenticationManager(http));
// 自定義 JSON 的 key
loginFilter.setUsernameParameter("username");
loginFilter.setPasswordParameter("password");
// 自定義接收的url,默認是login
// 此過濾器的doFilter是在AbstractAuthenticationProcessingFilter,在那里進行的url是否符合的判定
loginFilter.setFilterProcessesUrl("/api/auth/login");
// 設(shè)置login成功返回的JSON數(shù)據(jù)
loginFilter.setAuthenticationSuccessHandler(this::onAuthenticationSuccess);
// 設(shè)置login失敗返回的JSON數(shù)據(jù)
loginFilter.setAuthenticationFailureHandler(this::onAuthenticationFailure);
return loginFilter;
}
@Resource
private ObjectMapper objectMapper;
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException{
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
if(request.getRequestURI().endsWith("/login"))
out.write(objectMapper.writeValueAsString(JsonData.success("登錄成功")));
else if(request.getRequestURI().endsWith("/logout"))
out.write(objectMapper.writeValueAsString(JsonData.success("注銷成功")));
out.close();
}
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException{
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(objectMapper.writeValueAsString(JsonData.failure(401,exception.getMessage())));
out.close();
}
}
測試
文章來源地址http://www.zghlxwxcb.cn/news/detail-475301.html
二、總結(jié)
- HttpSecurity 中可以用來添加過濾器的三個方法:
-
addFilterAt(filter,atFilter)
:這個相當于線性表的插入操作,把 filter 插入在 atFilter 的位置上。 -
addFilterBefore(filter,atFilter)
:這個是在 atFilter 前插入一個 filter 過濾器; -
addFilterAfter(filter,atFilter)
:這個顧名思義是在 atFilter 后插入一個 filter 過濾器;
- 自定義登錄認證可以去繼承 UsernamePasswordAuthenticationFilter 過濾器重寫 attemptAuthentication 方法進行自定義,然后再在自定義的 SecurityConfig 配置類里面去將它配置到 Spring 容器中,注意實例化它后一定要給它內(nèi)部屬性 AuthenticationManager 進行初始化賦值,不然交給Spring容器管理的時候會報錯;最后添加到過濾器鏈中。
- 不去調(diào)用 formLogin 方法,那下面三個過濾器在過濾器鏈 SecurityFilterChain 中。
UsernamePasswordAuthenticationFilter:負責表單認證的;
DefaultLoginPageGeneratingFilter:提供登錄界面的;
DefaultLogoutPageGeneratingFilter:提供注銷界面的;
到了這里,關(guān)于【深入淺出Spring Security(五)】自定義過濾器進行前后端登錄認證的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!