一篇文章帶你使用 JSON 格式資料完成 SpringSecurity 登入
在使用 SpringSecurity
中,大夥都知道預設的登入資料是通過 key/value
的形式來傳遞的,預設情況下不支援 JSON 格式的登入資料,如果有這種需求,就需要自己來解決。
一、基本的登入方案
在說如何使用 JSON 登入之前,我們還是先來看看基本的登入吧,本文為了簡單,SpringSecurity 在使用中就不連線資料庫了,直接在記憶體中配置使用者名稱和密碼,具體操作步驟如下:
(1)建立 Spring Boot 工程
首先建立 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>
(2)新增 Security 配置
建立 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("許可權不足,訪問失敗");
resp.setStatus(403);
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
out.write(new ObjectMapper().writeValueAsString(error));
out.flush();
out.close();
}
});
}
}
這裡的配置雖然有點長,但是很基礎,配置含義也比較清晰,首先提供 BCryptPasswordEncoder
作為 PasswordEncoder ,可以實現對密碼的自動加密加鹽,非常方便,然後提供了一個名為 zhangsan 的使用者,密碼是 123 ,角色是 user ,最後配置登入邏輯,所有的請求都需要登入後才能訪問,登入介面是 /doLogin
,使用者名稱的 key 是 username ,密碼的 key 是 password ,同時配置登入成功、登入失敗以及登出成功、許可權不足時都給使用者返回JSON
提示,另外,這裡雖然配置了登入頁面為 /login
,實際上這不是一個頁面,而是一段 JSON
,在 LoginController
中提供該介面,如下:
@RestController
@ResponseBody
public class LoginController {
@GetMapping("/login")
public RespBean login() {
return RespBean.error("尚未登入,請登入");
}
@GetMapping("/hello")
public String hello() {
return "hello";
}
}
這裡 /login
只是一個 JSON 提示,而不是頁面, /hello
則是一個測試介面。
OK,做完上述步驟就可以開始測試了,執行 SpringBoot 專案,訪問 /hello
介面,結果如下:
此時先呼叫登入介面進行登入,如下:
登入成功後,再去訪問 /hello
介面就可以成功訪問了。
二、使用 JSON 登入
上面演示的是一種原始的登入方案,如果想將使用者名稱密碼通過 JSON 的方式進行傳遞,則需要自定義相關過濾器,通過分析原始碼我們發現,預設的使用者名稱密碼提取在 UsernamePasswordAuthenticationFilter
過濾器中,部分原始碼如下:
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);
}
//...
//...
}
從這裡可以看到,預設的使用者名稱/密碼提取就是通過 request 中的 getParameter
來提取的,如果想使用 JSON 傳遞使用者名稱密碼,只需要將這個過濾器替換掉即可,自定義過濾器如下:
public class MyAuthenticationFilter 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());
}
if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
//說明使用者以 JSON 的形式傳遞的引數
String username = null;
String password = null;
try {
Map<String, String> map = new ObjectMapper().readValue(request.getInputStream(), Map.class);
username = map.get("username");
password = map.get("password");
} catch (IOException e) {
e.printStackTrace();
}
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);
}
return super.attemptAuthentication(request, response);
}
}
這裡只是將使用者名稱/密碼的獲取方案重新修正下,改為了從 JSON 中獲取使用者名稱密碼,然後在 SecurityConfig 中作出如下修改:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin().permitAll()
.and().csrf().disable();
http.addFilterAt(myAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
MyAuthenticationFilter myAuthenticationFilter() throws Exception {
MyAuthenticationFilter filter = new MyAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}
}
使用 json 登入成功:
相關文章
- 一篇文章帶你認識 SpringSecuritySpringGse
- 一篇文章帶你入門Zookeeper
- 一篇文章帶你瞭解HTML格式化元素HTML
- 一篇文章帶你快速入門createjsJS
- # 一篇文章帶你入門軟體測試
- 一篇長文帶你在python裡玩轉Json資料PythonJSON
- 一篇文章帶你瞭解和使用Promise物件Promise物件
- 一篇文章帶你入門SQL程式設計GIFUSQL程式設計
- JSON資料格式的使用JSON
- 一篇文章帶你吃透 Docker 原理Docker
- 一篇文章帶你搞定 SpringSecurity 配置多個HttpSecurity 和實現對於方法安全的控制SpringGseHTTP
- 一篇帶你快速入門ansible和使用
- JSON 資料格式JSON
- 一篇文章帶你瞭解高質量代理ip的使用技巧
- 一篇文章帶你初步瞭解—CSS特指度CSS
- 一篇文章帶你瞭解HTML5 MathMLHTML
- 一篇文章帶你讀懂Redis的哨兵模式Redis模式
- MySQL十種鎖,一篇文章帶你全解析MySql
- 一篇文章帶你瞭解——Kotlin協程Kotlin
- 一篇文章帶你瞭解介面自動化
- 一篇文章帶你掌握效能測試工具——JmeterJMeter
- 一篇文章帶你弄懂Kerberos的設計思路ROS
- 一篇文章帶你玩轉正規表示式
- 使用.format格式化json格式的入參ORMJSON
- 使用redis完成註冊和登入Redis
- 【SpringSecurity OAuth2 JWT】實現SSO單點登入 第一篇SpringGseOAuthJWT
- 在.NET使用JSON作為資料交換格式JSON
- 一篇文章帶你瞭解CSS 分頁例項CSS
- 一篇文章帶你搞懂 etcd 3.5 的核心特性
- 一篇文章帶你吃透hashmap(面試指南升級版)HashMap面試
- 一篇文章帶你瞭解高可用架構分析架構
- php操作JSON格式資料PHPJSON
- 手把手帶你使用JWT實現單點登入JWT
- Android使用者登入功能完成Android
- Android中JSON資料格式的簡單使用AndroidJSON
- 一篇文章帶你掌握Flex佈局的所有用法Flex
- 一篇文章帶你瞭解設計模式——建立者模式設計模式
- springSecurity 登入以及使用者賬號密碼解析原理SpringGse密碼