持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天,点击查看活动详情
Spring Security在前后端分离项目的使用(认证篇)
在Spring Security中,框架内部使用的默认的登录方式是表单登录,传递的参数传递格式是键值对形式,但是这种登陆的方式并不适用于前后端分离的项目中,因为我们需要的是以JSON格式来传递登录数据,前端发送请求,后端处理请求后进行响应数据,这就需要我们自定义登录过滤器,来提取用户信息来实现认证。
注意:此处应确保过滤器来那种过滤器的顺序保持不变。
1.思路分析
首先,我们需要自定义的过滤器,重写父类方法的认证方法,父类的认证方法主要分三步:
- 1.校验登录请求的方式是否是post
- 2.校验请求的参数类型是否是Json格式的数据
- 3.提取用户名和密码进行认证
然后,我们需要自定义用户数据,用户数据肯定是放在数据库里面的,此处为了简单就还是用内存的方式进行登录,只不过要对原来的UserDetailsService进行替换。
其次,我们需要自定义登陆成功和失败的数据,这些数据当然也是Json格式的数据。
最后,我们需要用自定义的过滤器去替换默认的过滤器。
思路理清楚之后,我们嘴在手写代码。
2.代码
2.1 准备过滤器
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
@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格式的数据
if (request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)) {
Map<String, String> userInfo = new HashMap<>();
try {
//对Json格式进行处理,提取用户名密码
userInfo = new ObjectMapper().readValue(request.getInputStream(), Map.class);
String username = userInfo.get(getUsernameParameter());
String password = userInfo.get(getPasswordParameter());
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
} catch (IOException e) {
e.printStackTrace();
}
}
return super.attemptAuthentication(request, response);
}
}
从自定义的过滤器可以看出,与默认的UsermamePasswordAuthenticationFiter过滤器处理方式很像,只是替换了对Json的处理。
2.1 编写配置类
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//自定义用户信息,当然,最好是放在数据库里,这里只是模拟一下
//需要注入到容器里
@Bean
public UserDetailsService userDetailsService(){
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
inMemoryUserDetailsManager.createUser(User.withUsername("root").password("{noop}123").roles("admin").build());
return inMemoryUserDetailsManager;
}
//替换用户的数据源
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public LoginFilter loginFilter() throws Exception {
LoginFilter loginFilter = new LoginFilter();
loginFilter.setUsernameParameter("username");
loginFilter.setPasswordParameter("password");//用户名和密码的参数
loginFilter.setAuthenticationManager(authenticationManagerBean());//认证器
loginFilter.setAuthenticationSuccessHandler(((request, response, authentication) -> {
Map<String,Object> result = new HashMap<>();
result.put("msg","登录成功");
result.put("userInfo",authentication.getPrincipal());
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
String s = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(s);
}));//登录成功的Json数据
loginFilter.setAuthenticationFailureHandler(((request, response, exception) -> {
Map<String,Object> result = new HashMap<>();
result.put("msg","登录失败"+exception.getMessage());
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
String s = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(s);
}));//登录失败的Json数据
return loginFilter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin() //开启表单认证
.and()
.csrf().disable();
http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
准备好过滤器后,我们需要对它进行配置,来解释一下这些代码:
- userDetailsService():自定义用户信息,当然,最好是放在数据库里,这里只是覆盖了内存的用户信息,只是为了模拟一下。可以集成mybatis等持久层框架完成数据源的更改。
- configure(AuthenticationManagerBuilder auth)以及authenticationManagerBean()分别是用来将数据源进行替换以及注入到容器里。
- loginFilter():是对过滤器进行配置,此处配置的有用户名和密码的参数;数据源的具体实现方式以及接受认证器的实例;登录成功和失败的信息;
- configure():拦截所有请求,但是/login请求不会拦截,开启表单认证;最后替换过滤器的是实现,addFilterAt()方法,就是在当前过滤器的位置进行替换。
启动项目,用PostMan进行请求
[psot] localhost:8080/login
{"username":"root","password":"123"}
登录成功或者失败就会返回对应的信息了,注意看状态码是200,表示请求成功,登录后再次请求系统资源就会带上右上角的Cookies信息,就不用再认证了。登录失败就不展示了,状态码会返回500,并提示"登录失败Bad credentials",其实就是凭证错误。