SpringSecurity获取JSON格式的用户名密码
SpringSecurity环境
pom.xml
pom.xml依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- SpringSecurity的依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
自定义获取用户名和密码
SpringSecurity自带的方式
UsernamePasswordAuthenticationFilter.java 这个类是处理身份验证表单提交
其中的方法
attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
就是获取用户名和密码的方法
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
username = (username != null) ? username : "";
username = username.trim();
String password = obtainPassword(request);
password = (password != null) ? password : "";
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
其中有两个方法obtainPassword(),obtainUsername()这就是SpringSecurity获取用户名和密码的方法
@Nullable
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(this.passwordParameter);
}
@Nullable
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(this.usernameParameter);
}
可以发现默认的是获取param里的内容,如果是JSON格式的用户名和密码的话是无法获取的,之后我们可以用一个类
继承 UsernamePasswordAuthenticationFilter
并重写其中的
attemptAuthentication
方法 获取JSON格式的用户名和密码,你也可重写obtainPassword,obtainUsername这两个方法达成同样的目的。
自定义获取用户名密码的方式
获取JSON格式的用户名密码
新建MyUsernamePasswordAuthenticationFilter继承UsernamePasswordAuthenticationFilter
重写attemptAuthentication方法
public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username = null;
String password = null;
try {
JSONObject jsonObject = JsonUtils.getRequestJsonObject(request);
System.out.println(jsonObject);
username = String.valueOf(jsonObject.get("username"));
password = String.valueOf(jsonObject.get("password"));
username = (username != null) ? username : "";
password = (password != null) ? password : "";
} catch (IOException e) {
e.printStackTrace();
}
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
在SecurityConfiguration中配置他
@Bean
public UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() throws Exception {
UsernamePasswordAuthenticationFilter filter = new MyUsernamePasswordAuthenticationFilter();
//这里设置自带的AuthenticationManager,否则要自己写一个
filter.setAuthenticationManager(authenticationManagerBean());
//设置处理成功处理器
filter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
String message = "登录成功";
PrintWriter printWriter = response.getWriter();
printWriter.write(message);
printWriter.flush();
printWriter.close();
}
});
//设置过滤器拦截路径
filter.setFilterProcessesUrl("/newlogin");
return filter;
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
我们使用postmen测试 http://localhost:8080/newlogin
这里解释一下为什么这么配置
先看一下不自定义的配置
.and()
//开启登录
.formLogin()
//登录页面路径
.loginPage("/toLogin")
//执行登录的路径
.loginProcessingUrl("/login")
//登录成功后转发页面
.successForwardUrl("/index")
//.successHandler()
.permitAll()
这里的 .loginProcessingUrl(“/login”)在源码中是调用
setRequiresAuthenticationRequestMatcher
方法
public T loginProcessingUrl(String loginProcessingUrl) {
this.loginProcessingUrl = loginProcessingUrl;
this.authFilter.setRequiresAuthenticationRequestMatcher(createLoginProcessingUrlMatcher(loginProcessingUrl));
return getSelf();
}
最后实际上调用的是下面的第二个方法
自定义过滤器中 filter.setFilterProcessesUrl(“/newlogin”) 最后调用的是第一方法然后调用第二个方法,也就是可以说等于 .loginProcessingUrl(“/login”)
public void setFilterProcessesUrl(String filterProcessesUrl) {
setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(filterProcessesUrl));
}
public final void setRequiresAuthenticationRequestMatcher(RequestMatcher requestMatcher) {
Assert.notNull(requestMatcher, "requestMatcher cannot be null");
this.requiresAuthenticationRequestMatcher = requestMatcher;
}
也就是说我们配置 filter.setFilterProcessesUrl(“/newlogin”) 就是在配置 .loginProcessingUrl(“/login”)
只不过 filter.setFilterProcessesUrl(“/newlogin”)是在配置我们自定义的过滤器,而 .loginProcessingUrl(“/login”)是在配置自带的过滤器。实际是在进行同样配置操作。
这里.successForwardUrl(“/index”)和.successHandler()
这个被我注释掉了
,在源码中也是差不多的操作
public FormLoginConfigurer<H> successForwardUrl(String forwardUrl) {
successHandler(new ForwardAuthenticationSuccessHandler(forwardUrl));
return this;
}
public final T successHandler(AuthenticationSuccessHandler successHandler) {
this.successHandler = successHandler;
return getSelf();
}
通过上面代码我们可以看出.successForwardUrl(“/index”)实际上就是调用了successHandler(),只不过用了一个ForwardAuthenticationSuccessHandler封装了一下。
再看看我们对自定义过滤器的配置一下就明白了
//设置处理成功处理器
filter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
String message = "登录成功";
PrintWriter printWriter = response.getWriter();
printWriter.write(message);
printWriter.flush();
printWriter.close();
}
});
漏了一个配置这里补一下,配置在configure(HttpSecurity http)方法中的。
//在UsernamePasswordAuthenticationFilter之前调用我们自定义的过滤器
http.addFilterAt(usernamePasswordAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class);
举一反三我们可以有更多的设置
登出成功处理器
.and()
//开启登出
.logout()
//执行登出路径
.logoutUrl("/logout")
//登出成功后转发页面
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
String message = "登出成功";
PrintWriter printWriter = response.getWriter();
printWriter.write(message);
printWriter.flush();
printWriter.close();
}
})
登陆成功和失败处理器
@Bean
public UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() throws Exception {
UsernamePasswordAuthenticationFilter filter = new MyUsernamePasswordAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
String message = "登录成功";
PrintWriter printWriter = response.getWriter();
printWriter.write(message);
printWriter.flush();
printWriter.close();
}
});
filter.setAuthenticationFailureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
String message = "登录失败";
PrintWriter printWriter = response.getWriter();
printWriter.write(message);
printWriter.flush();
printWriter.close();
}
});
filter.setFilterProcessesUrl("/newlogin");
return filter;
}
未登录账号处理
http.addFilterAt(usernamePasswordAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(new AuthenticationEntryPoint() {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setContentType("application/json;charset=UTF-8");
String message = "请先进行登录";
PrintWriter printWriter = response.getWriter();
printWriter.write(message);
printWriter.flush();
printWriter.close();
}
})
;