SpringSecurity登录使用JSON格式数据的方法

在使用SpringSecurity中,大伙都知道默认的登录数据是通过key/value的形式来传递的,默认情况下不支持JSON格式的登录数据,如果有这种需求,就需要自己来解决,本文主要和小伙伴来聊聊这个话题。

基本登录方案

在说如何使用JSON登录之前,我们还是先来看看基本的登录吧,本文为了简单,SpringSecurity在使用中就不连接数据库了,直接在内存中配置用户名和密码,具体操作步骤如下:

创建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>

添加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 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类加入进来即可,接下来就可以使用JSON进行登录了,如下:

好了,本文就先介绍到这里,有问题欢迎留言讨论。 希望对大家的学习有所帮助,也希望大家多多支持。

以上是 SpringSecurity登录使用JSON格式数据的方法 的全部内容, 来源链接: utcz.com/z/323920.html

回到顶部