SpringSecurity登錄使用JSON格式數(shù)據(jù)的方法
在使用SpringSecurity中,大伙都知道默認(rèn)的登錄數(shù)據(jù)是通過(guò)key/value的形式來(lái)傳遞的,默認(rèn)情況下不支持JSON格式的登錄數(shù)據(jù),如果有這種需求,就需要自己來(lái)解決,本文主要和小伙伴來(lái)聊聊這個(gè)話題。
基本登錄方案
在說(shuō)如何使用JSON登錄之前,我們還是先來(lái)看看基本的登錄吧,本文為了簡(jiǎn)單,SpringSecurity在使用中就不連接數(shù)據(jù)庫(kù)了,直接在內(nèi)存中配置用戶名和密碼,具體操作步驟如下:
創(chuàng)建Spring Boot工程
首先創(chuàng)建SpringBoot工程,添加SpringSecurity依賴,如下:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
添加Security配置
創(chuàng)建SecurityConfig,完成SpringSecurity的配置,如下:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("zhangsan").password("$2a$10$2O4EwLrrFPEboTfDOtC0F.RpUMk.3q3KvBHRx7XXKUMLBGjOOBs8q").roles("user");
}
@Override
public void configure(WebSecurity web) throws Exception {
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/doLogin")
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
RespBean ok = RespBean.ok("登錄成功!",authentication.getPrincipal());
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(new ObjectMapper().writeValueAsString(ok));
out.flush();
out.close();
}
})
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
RespBean error = RespBean.error("登錄失敗");
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(new ObjectMapper().writeValueAsString(error));
out.flush();
out.close();
}
})
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
RespBean ok = RespBean.ok("注銷成功!");
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(new ObjectMapper().writeValueAsString(ok));
out.flush();
out.close();
}
})
.permitAll()
.and()
.csrf()
.disable()
.exceptionHandling()
.accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest req, HttpServletResponse resp, AccessDeniedException e) throws IOException, ServletException {
RespBean error = RespBean.error("權(quán)限不足,訪問(wèn)失敗");
resp.setStatus(403);
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(new ObjectMapper().writeValueAsString(error));
out.flush();
out.close();
}
});
}
}
這里的配置雖然有點(diǎn)長(zhǎng),但是很基礎(chǔ),配置含義也比較清晰,首先提供BCryptPasswordEncoder作為PasswordEncoder,可以實(shí)現(xiàn)對(duì)密碼的自動(dòng)加密加鹽,非常方便,然后提供了一個(gè)名為zhangsan的用戶,密碼是123,角色是user,最后配置登錄邏輯,所有的請(qǐng)求都需要登錄后才能訪問(wèn),登錄接口是/doLogin,用戶名的key是username,密碼的key是password,同時(shí)配置登錄成功、登錄失敗以及注銷成功、權(quán)限不足時(shí)都給用戶返回JSON提示,另外,這里雖然配置了登錄頁(yè)面為/login,實(shí)際上這不是一個(gè)頁(yè)面,而是一段JSON,在LoginController中提供該接口,如下:
@RestController
@ResponseBody
public class LoginController {
@GetMapping("/login")
public RespBean login() {
return RespBean.error("尚未登錄,請(qǐng)登錄");
}
@GetMapping("/hello")
public String hello() {
return "hello";
}
}
這里/login只是一個(gè)JSON提示,而不是頁(yè)面, /hello則是一個(gè)測(cè)試接口。
OK,做完上述步驟就可以開始測(cè)試了,運(yùn)行SpringBoot項(xiàng)目,訪問(wèn)/hello接口,結(jié)果如下:

此時(shí)先調(diào)用登錄接口進(jìn)行登錄,如下:

登錄成功后,再去訪問(wèn)/hello接口就可以成功訪問(wèn)了。
使用JSON登錄
上面演示的是一種原始的登錄方案,如果想將用戶名密碼通過(guò)JSON的方式進(jìn)行傳遞,則需要自定義相關(guān)過(guò)濾器,通過(guò)分析源碼我們發(fā)現(xiàn),默認(rèn)的用戶名密碼提取在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)的用戶名/密碼提取就是通過(guò)request中的getParameter來(lái)提取的,如果想使用JSON傳遞用戶名密碼,只需要將這個(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);
}
}
}
這里只是將用戶名/密碼的獲取方案重新修正下,改為了從JSON中獲取用戶名密碼,然后在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.setContentType("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;
}
將自定義的CustomAuthenticationFilter類加入進(jìn)來(lái)即可,接下來(lái)就可以使用JSON進(jìn)行登錄了,如下:

好了,本文就先介紹到這里,有問(wèn)題歡迎留言討論。 希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
如何基于spring security實(shí)現(xiàn)在線用戶統(tǒng)計(jì)
這篇文章主要介紹了如何基于spring security實(shí)現(xiàn)在線用戶統(tǒng)計(jì),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
搭建maven私有倉(cāng)庫(kù)的方法實(shí)現(xiàn)
Maven是一個(gè)流行的Java項(xiàng)目管理工具,它可以幫助我們管理項(xiàng)目的構(gòu)建、報(bào)告和文檔,本文主要介紹了搭建maven私有倉(cāng)庫(kù)的方法實(shí)現(xiàn),感興趣的可以了解一下2023-05-05
解決Spring?Boot應(yīng)用打包后文件訪問(wèn)問(wèn)題
在Spring Boot項(xiàng)目的開發(fā)過(guò)程中,一個(gè)常見的挑戰(zhàn)是如何有效地訪問(wèn)和操作資源文件,本文就來(lái)介紹一下解決Spring?Boot應(yīng)用打包后文件訪問(wèn)問(wèn)題,感興趣的可以了解一下2024-01-01
Java虛擬機(jī)內(nèi)存溢出與內(nèi)存泄漏
這篇文章主要介紹了Java虛擬機(jī)內(nèi)存溢出與內(nèi)存泄漏,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
java實(shí)現(xiàn)上傳圖片尺寸修改和質(zhì)量壓縮
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)上傳圖片尺寸修改和質(zhì)量壓縮,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
必知必會(huì)的SpringBoot實(shí)現(xiàn)熱部署兩種方式
這篇文章主要為大家介紹了必知必會(huì)的SpringBoot實(shí)現(xiàn)熱部署兩種方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
Java中隊(duì)列Queue和Deque的區(qū)別與代碼實(shí)例
學(xué)過(guò)數(shù)據(jù)結(jié)構(gòu)的,一定對(duì)隊(duì)列不陌生,java也實(shí)現(xiàn)了隊(duì)列,下面這篇文章主要給大家介紹了關(guān)于Java中隊(duì)列Queue和Deque區(qū)別的相關(guān)資料,需要的朋友可以參考下2021-08-08
java實(shí)現(xiàn)漢字轉(zhuǎn)unicode與漢字轉(zhuǎn)16進(jìn)制實(shí)例
這篇文章主要介紹了java實(shí)現(xiàn)漢字轉(zhuǎn)unicode與漢字轉(zhuǎn)16進(jìn)制的實(shí)現(xiàn)方法,是Java操作漢字編碼轉(zhuǎn)換的一個(gè)典型應(yīng)用,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-10-10
解讀spark添加二方包導(dǎo)致依賴沖突排查問(wèn)題
這篇文章主要介紹了spark添加二方包導(dǎo)致依賴沖突排查問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06
idea版本控制忽略.idea文件和.iml文件的問(wèn)題
這篇文章主要介紹了idea版本控制忽略.idea文件和.iml文件,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11

