- 前后端分离是企业级应用开发的主流,登录功能同样采用前后端分离模式。
- 用户信息存在数据库中。
- Spring Security提供身份认证。
- 前后端交互通过JSON进行。
- 登录成功不是页面跳转,而是一段JSON提示。
#第一步:前期准备工作 项目架构:
- 开发环境Spring-boot 2.5.5
- Maven
- 数据库MySQL8.0+,存放用户、角色、用户角色对照表
- 持久层Mybatis
https://blog.csdn.net/m0_59562547/article/details/120864233
#第二步:WebSecurityJsonConfig核心配置类- 首先注入UserService用于userDetailsService用户身份认证(包含密码、角色、锁定等)。
- 再配置用户登录密码需要BCryptPasswordEncoder密文认证。
- 再配置自定义登录参数loginid和passwd。
- successHandler为成功登陆后操作内容。这里我们获取当前登录用户主体信息principal,设置返回编码格式,将resp状态设置为200,将登录用户信息装入返回内容,并使用springboot自带的Jackson的objectMapper.writevalueAsString()方法将内容转化为JSON格式。
- failureHandler为登录失败后操作内容。这里我们设置返回编码格式,将resp状态设置为401,并根据exception类型输出返回内容,并转化为JSON格式。
- 最后我们设置所有接口不需要认证可访问,再关闭csrf。
package com.example.springsecurity.Config;
import com.example.springsecurity.Service.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class WebSecurityJsonConfig extends WebSecurityConfigurerAdapter {
//注入用户服务
@Autowired
UserService userService;
//配置用户登录密码需要BCryptPasswordEncoder密文认证
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder(10);
}
//基于数据库的用户账号密码、角色、过期、锁定等认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.userDetailsService(userService);
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception{
httpSecurity.authorizeRequests()
.and()
//开启表单登录,登录URL为/login
.formLogin()
.loginProcessingUrl("/login")
//自定义登录参数用户名loginid密码passwd
.usernameParameter("loginid")
.passwordParameter("passwd")
//身份认证成功,采用lambda写法,传入req,resp,auth参数
.successHandler((request, response, authentication) -> {
//获取当前登录用户主体信息,包括用户名密码,角色,账号状态
Object principal = authentication.getPrincipal();
//设置返回编码格式,使用PrintWriter方法输出
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
//设置resp返回状态,成功为200
response.setStatus(200);
Map map =new HashMap<>();
//设置返回内容
map.put("status",200);
map.put("msg",principal);
//使用springboot自带的Jackson转化并输出JSON信息
ObjectMapper objectMapper=new ObjectMapper();
out.write(objectMapper.writevalueAsString(map));
out.flush();
out.close();
})
//身份认证失败,采用lambda写法,传入req,resp,exception参数
.failureHandler((request, response, exception) -> {
//设置返回编码格式,使用PrintWriter方法输出
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
//设置resp返回状态,失败为401
response.setStatus(401);
Map map = new HashMap<>();
map.put("status",401);
//根据错误状态设置返回内容
if(exception instanceof LockedException){
map.put("msg","账户被锁定,登陆失败!");
}else if (exception instanceof BadCredentialsException){
map.put("msg","账户名或者密码输入错误,登录失败!");
}else if (exception instanceof DisabledException){
map.put("msg","账户被禁用,登陆失败!");
}else if(exception instanceof AccountExpiredException){
map.put("msg","账户已过期,登陆失败!");
}else if(exception instanceof CredentialsExpiredException){
map.put("msg","密码已过期,登陆失败!");
}else {
map.put("msg","登陆失败!");
}
//使用springboot自带的Jackson转化并输出JSON信息
ObjectMapper objectMapper =new ObjectMapper();
out.write(objectMapper.writevalueAsString(map));
out.flush();
out.close();
})
//接口不需要认证可以访问
.permitAll()
.and()
//关闭csrf
.csrf()
.disable();
}
}
#第三步:启动项目开始测试
启动MySQL数据库和Spring-boot项目,使用POSTMAN进行测试,登录请求的用户名参数是loginid,密码是passwd,使用POST请求,登录成功后返回用户的基本信息(JSON格式)。
请求网址为http://localhost:8080/login?loginid=admin&passwd=007007,效果如下图:
密码错误,效果如下图:



