前言
Spring Security是一个功能强大且高度且可定制的身份验证和访问控制框架,包含标准的身份认证和授权。
本文主要介绍SpringBoot中如何配置使用 Spring Security 安全认证框架并简述相关原理和步骤。
核心认证流程解析

- 请求过滤
- 用户提交登录表单
AbstractAuthenticationProcessingFilter过滤请求,创建AbstractAuthenticationToken
以默认提供的 UsernamePasswordAuthenticationFilter举例:
ini
public class UsernamePasswordAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
public UsernamePasswordAuthenticationFilter() {
// 设置过滤规则
super(new AntPathRequestMatcher("/login", "POST"));
}
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
// ......
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
// 根据请求参数设置 UsernamePasswordAuthenticationToken 实例
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
// ......
}
- 认证管理器
AuthenticationManager(通常是ProviderManager)协调认证过程,根据AbstractAuthenticationToken类型 选择对应的AuthenticationProvider
在 ProviderManager中根据不同的 Token 类型匹配不同的 Provider
ini
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
AuthenticationException parentException = null;
Authentication result = null;
Authentication parentResult = null;
boolean debug = logger.isDebugEnabled();
// 遍历所有已注册的 providers ,匹配能处理当前 authentication 的 provider 进行认证处理
for (AuthenticationProvider provider : getProviders()) {
// 判断provider是否可以处理当前的authentication
if (!provider.supports(toTest)) {
continue;
}
try {
// 执行认证逻辑,返回认证结果
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
} catch (AccountStatusException | InternalAuthenticationServiceException e) {
prepareException(e, authentication);
throw e;
} catch (AuthenticationException e) {
lastException = e;
}
}
// .......
throw lastException;
}
- 匹配到对应的 provider 后,调用
provider.authenticate(authentication);执行实际的认证过程。
认证过程以
AbstractUserDetailsAuthenticationProvider(实现DaoAuthenticationProvider)为例,大致过程为:
- getUserDetailsService().loadUserByUsername(username); 加载用户信息
- preAuthenticationChecks.check(user); 校验用户信息,是否锁定、过期、可用等
- additionalAuthenticationChecks(user,authentication); 验证用户密码
- 认证后处理
- 认证成功:生成已认证的
Authentication对象,存入SecurityContext - 认证失败:抛出
AuthenticationException由AuthenticationEntryPoint处理
| 组件 | 职责 | 典型实现类 |
|---|---|---|
AbstractAuthenticationProcessingFilter |
拦截认证请求,封装认证对象 | UsernamePasswordAuthenticationFilter |
AuthenticationManager |
认证流程协调者 | ProviderManager |
AuthenticationProvider |
执行具体认证逻辑 | DaoAuthenticationProvider |
UserDetailsService |
加载用户数据 | 自定义实现类 |
其中 UsernamePasswordAuthenticationFilter和UsernamePasswordAuthenticationToken为框架自带的 登录请求过滤和Token实例。
如需自定义登录请求过滤和Token实例,可自行实现 AbstractAuthenticationProcessingFilter和 AbstractAuthenticationToken接口。
不同的 AbstractAuthenticationToken 通常有不同的 AuthenticationProvider与之对应,用于实现不同的认证逻辑。
自定义的认证逻辑中,通常都是对 AbstractAuthenticationToken和 AuthenticationProvider 的不同实现。
JWT
JWT(JSON Web Token) 是一种轻量级的开放标准(RFC 7519),用于在网络应用间安全地传输信息。它通常用于 身份认证(Authentication) 和 数据交换(Information Exchange),特别适合 前后端分离 和 无状态(Stateless) 的应用场景。
JWT在登录中的应用过程
- 用户提交 用户名 + 密码 登录
- 服务器验证后,生成 JWT 并返回给客户端
- 客户端存储 JWT(通常放
localStorage或Cookie) - 后续请求 在
Authorization头携带 JWT - 服务器 验证 JWT 签名,并解析数据
特点
- 防篡改:签名(Signature)确保 Token 未被修改
- 可设置有效期(
exp字段)避免长期有效 - 无状态:服务器不需要存储 Session,适合分布式系统
自定义认证逻辑
接下来根据 Spring Security 的认证过程和JWT的特点进行自定义登录逻辑的编写
核心类图

登录流程

具体实现
JwtLoginFilter
实现 AbstractAuthenticationProcessingFilter接口
java
public class JwtLoginFilter extends AbstractAuthenticationProcessingFilter {
public JwtLoginFilter() {
// 设置当前 Filter ,也就是需要过滤的登录URL
super(new AntPathRequestMatcher("/auth/login", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
// 获取前端传递登录模式
String loginType = request.getParameter("loginType");
Authentication authentication = null;
// 判断前端使用的登录模式
if (CommonConstant.LoginType.SMS.equals(loginType)) {
// 手机短信
String phone = request.getParameter("phone");
String code = request.getParameter("code");
authentication = new SmsAuthenticationToken(phone, code);
}
if (CommonConstant.LoginType.WX.equals(loginType)) {
String code = request.getParameter("code");
authentication = new WxAuthenticationToken(code);
}
if (authentication == null) {
throw new UnsupportedLoginTypeException();
}
return getAuthenticationManager().authenticate(authentication);
}
}
SmsAuthenticationToken
TIPS:如果需要多种认证模式,如:用户密码、短信认证、扫描登录、三方认证等,可实现不同的
Token实例,并实现与之对应的Provider。
以短信认证Token SmsAuthenticationToken举例
typescript
public class SmsAuthenticationToken extends AbstractAuthenticationToken {
private final String phone;
private final String code;
public SmsAuthenticationToken(String phone, String code) {
super(new ArrayList<>());
this.phone = phone;
this.code = code;
}
@Override
public String getCredentials() {
return code;
}
@Override
public String getPrincipal() {
return phone;
}
}
SmsAuthProvider
具体实现短信认证的逻辑,主要工作原理是将前端传递的手机号和短信验证码进行匹配校验,如果合法,则认证成功,如果不合法,返回认证失败。
java
@Component
public class SmsAuthProvider implements AuthenticationProvider {
@Autowired
private AbstractLogin abstractLogin;
@Autowired
private JwtUtil jwtUtil;
/**
* 验证手机验证码登录认证
*
* @param authentication the authentication request object.
* @return
* @throws AuthenticationException
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
SmsAuthenticationToken token = (SmsAuthenticationToken) authentication;
String phone = token.getPrincipal();
String code = token.getCredentials();
try {
UserDetails userDetails = abstractLogin.smsLogin(phone, code);
token.setDetails(userDetails);
} catch (AuthenticationException authenticationException) {
throw authenticationException;
} catch (Exception e) {
throw new LoginFailException();
}
return token;
}
@Override
public boolean supports(Class<?> authentication) {
return SmsAuthenticationToken.class.equals(authentication);
}
}
SpringBoot配置过程
我们已在上述的过程中将核心的认证逻辑实现,接下来就是把对应的代码配置到 Spring Security 工程之中。
JwtAuthConfig
JwtAuthConfig实现 WebSecurityConfigurerAdapter作为整体的配置入口
less
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class JwtAuthConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtLoginConfig loginConfig;
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.formLogin()
.disable()
// 应用登录相关配置信息
.apply(loginConfig)
.and()
.authorizeRequests()
// 放行登录 URL
.antMatchers("/auth/login")
.permitAll();
}
}
该配置类中,只做了初始化的简单配置,如设置放行登录URL、禁用 csrf、禁用 默认的formLogin等。更多的登录认证配置在 JwtLoginConfig中进行。
JwtLoginConfig
JwtLoginConfig实现了SecurityConfigurerAdapter,SecurityConfigurerAdapter 是 Spring Security 的核心配置基类,用于自定义安全规则(如认证、授权、过滤器链等)。
配置信息如下:
scss
@Configuration
public class JwtLoginConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
@Autowired
private LoginSuccessHandler successHandler;
@Autowired
private LoginFailHandler failHandler;
@Autowired
private JwtProviderManager jwtProviderManager;
/**
* 将登录接口的过滤器配置到过滤器链中
* 1. 配置登录成功、失败处理器
* 2. 配置自定义的userDetailService(从数据库中获取用户数据)
* 3. 将自定义的过滤器配置到spring security的过滤器链中,配置在UsernamePasswordAuthenticationFilter之前
* @param http
*/
@Override
public void configure(HttpSecurity http) {
JwtLoginFilter filter = new JwtLoginFilter();
// authenticationManager 中已经预设系统内的 provider 集合
filter.setAuthenticationManager(jwtProviderManager);
//认证成功处理器
filter.setAuthenticationSuccessHandler(successHandler);
//认证失败处理器
filter.setAuthenticationFailureHandler(failHandler);
//将这个过滤器添加到UsernamePasswordAuthenticationFilter之后执行
http.addFilterAfter(filter, UsernamePasswordAuthenticationFilter.class);
}
}
JwtProviderManager
从上文中的认证过程(时序图)中,AuthenticationManager 是委托 ProviderManager进行认证模式的匹配和执行对应的 provider。
:::tips 为了后续的多认证模式的支持和动态匹配,所以将 ProviderManager 交给 Spring 容器管理,并且通过构造方法将平台内所有已经注册到Spring容器中的 provider进行注入,以达到自动装配的目的。
注:暂只做简单实现。
java
@Component
public class JwtProviderManager extends ProviderManager {
public JwtProviderManager(List<AuthenticationProvider> providers) {
super(providers);
}
}
LoginSuccessHandler
认证成功后,对认证结果生成JWT Token 返回前端
ini
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
@Autowired
private JwtUtil jwtUtil;
@Autowired
private NacosHcUserConfigProperties configProperties;
@Override
public void onAuthenticationSuccess(
HttpServletRequest request,
HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {
// 生成 token 返回前端
// Object principal = authentication.getPrincipal();
UserDetails details = (UserDetails) authentication.getDetails();
// accessToken 过期时间 30分钟
Long accessTokenExpireSeconds = configProperties.getAuth().getAccessTokenExpireSeconds();
// refreshToken 过期时间 6小时
Long refreshTokenExpireSeconds = configProperties.getAuth().getRefreshTokenExpireSeconds();
String accessToken = jwtUtil.createToken(details.getUsername(), accessTokenExpireSeconds);
String refreshToken = jwtUtil.createToken(accessToken, refreshTokenExpireSeconds);
Map<String, String> tokenMap = new HashMap<>();
tokenMap.put("accessToken", accessToken);
tokenMap.put("refreshToken", refreshToken);
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.getWriter().write(JSON.toJSONString(ApiResult.success(tokenMap)));
}
}
END
至此,相关 Spring Security 配置已完成!💯💯
🧑💻🧑💻🧑💻
下一篇继续探究 Spring Security 在登录后的认证鉴权过程。