Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
本节介绍Spring Security在Servlet身份验证中使用的主要架构组件。如果需要解释这些部分如何组合在一起的具体流程,请查看特定于身份验证机制的部分。
关系的简要示意图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sfrsO9wC-1654844540950)(https://app.yinxiang.com/FileSharing.action?hash=1/c73daea197b506c37fd4b8a220689411-31431)]
SecurityContextHolder 示意图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JIauoIE6-1654844540952)(https://app.yinxiang.com/FileSharing.action?hash=1/e5736a02d5ccb739ae0bc4698fa58f48-22163)]
这是 WebSecurityConfigurerAdapter 的初始化加载流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y6FcKHhY-1654844540952)(https://app.yinxiang.com/FileSharing.action?hash=1/ad820c1970141c44e14862330e49d6d5-73403)]
从上侧流程图我们可以大致了解 Spring Security 的架构应该如下
其中 FilterChain 中包含各种各样的 filter
登陆成功流程大致如下图
AbstractAuthenticationProcessingFilter
将从被验证的 HttpServletRequest
处创建一个 Authentication
。Authentication
类型的创建工作则依赖于
AbstractAuthenticationProcessingFilter
的子类。例如
UsernamePasswordAuthenticationFilter
从已提交的 HttpServletRequest
中获取用户名密码并创建
UsernamePasswordAuthenticationToken
Authentication
传递给 AuthenticationManager
进行身份验证。SecurityContextHolder
RememberMeServices
.loginFail
。如果没有配置 remeberme ,则不会进行任何操作AuthenticationFailureHandler
SessionAuthenticationStrategy
收到新登录通知。SecurityContextHolder
设置 Authentication
。稍后 SecurityContextPersistenceFilter
将 SecurityContext
保存到HttpSession
中.RememberMeServices
.loginSuccess
。如果没有配置 remeberme ,则不会进行任何操作ApplicationEventPublisher
发布 InteractiveAuthenticationSuccessEvent
事件AuthenticationSuccessHandler
。
pom.xml
引入 Spring Security 时我们需要在 pom.xml
中添加
<!--Spring Security 包引入-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--Spring Boot Web 包引入-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Spring Boot 测试包引入-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--Spring Security 测试包引入-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
application.yml
在 application.yml 中编写配置文件
server:
port: 8088 # 服务器端口
配置
WebSecurityConfigurerAdapter
类
// Step1: 创建自己的 WebSecurityConfigurerAdapter
@Configuration
public class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
// Step2: 重写 configure 方法
@Override
public void configure(HttpSecurity http){
}
}
配置类
WebSecurityConfigurerAdapter
// 注入密码加密器
@Bean
PasswordEncoder passwordEncoder(){
// return new BCryptPasswordEncoder();
// return new Argon2PasswordEncoder();
// return new SCryptPasswordEncoder();
return new Pbkdf2PasswordEncoder();
}
使用加密器对密码进行加密以及密码之间的匹配
String password = "RawPassword";
String pwdCrypt = passwordEncoder.encode(password);
passwordEncoder.matches(password,pwdCrypt);
配置
WebSecurityConfigurerAdapter
类
@Override
public void configure(HttpSecurity http){
try {
http.formLogin()
.usernameParameter("usr")// 设定登陆时用户名的参数名,默认为 username
.passwordParameter("pwd")// 设定登陆时密码的参数名,默认为 password
.loginPage("login.html") // 设定登陆页面
.loginProcessingUrl("/login") // 处理登陆的 url
.successForwardUrl("/loginSuccess") // 登陆成功后跳转的 url
.successHandler(new AuthenticationSuccessHandler() { // 登陆成功的处理器
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
}
}).failureForwardUrl("/loginFailure") // 失败跳转的 url
.failureHandler(new AuthenticationFailureHandler() { // 失败的请求处理器
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
配置
WebSecurityConfigurerAdapter
类
@Override
public void configure(HttpSecurity http){
try {
http.authorizeRequests()
.antMatchers(HttpMethod.POST,"/login").permitAll()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.antMatchers("/status").permitAll() // 允许所有请求通过访问
.regexMatchers("*.png").permitAll() // 通过正则表达式进行匹配
.antMatchers("/userResource").hasAuthority("USER") // 具有某个权限后方可访问
.antMatchers("/userResource1").hasAnyAuthority("SYS","USER") // 具有任意一个权限后方可访问
.antMatchers("/roleResource").hasRole("role1") // 具有某个角色后方可访问,角色的权限名称为 “ROLE_” 前缀加上角色名称
.antMatchers("/roleResource1").hasAnyRole("role1","role2")// 具有任意一个角色后方可访问
.antMatchers("/accessAuthority").access("hasAnyAuthority(SYS,USER)")// 具有任意一个权限后方可访问
.antMatchers("/accessRoles").access("hasAnyRole(role1,role2)")// 具有任意一个角色后方可访问
.anyRequest().authenticated(); // 其余任何请求都需要经过访问
} catch (Exception e) {
e.printStackTrace();
}
}
配置
WebSecurityConfigurerAdapter
类
@Override
public void configure(HttpSecurity http){
try {
http.rememberMe().rememberMeParameter("remeberme")
.tokenRepository(new InMemoryTokenRepositoryImpl()) // 设置 Token 仓库,用于存储,更新,和获得 Token
.userDetailsService(username -> // 配置验证时的 UserDetailService
new User(username,"pwd", AuthorityUtils.commaSeparatedStringToAuthorityList("sys")))
.tokenValiditySeconds(1800);//token 有效时间
} catch (Exception e) {
e.printStackTrace();
}
}
UserDetailsService
接口
创建 MyUserDetailsService
类并且实现 UserDetailsService
接口
@Service
public class MyUserDetailsService implements UserDetailsService{
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String password = "pwd";
String pwdCrypt = passwordEncoder.encode(password);// 模拟从数据中取出密码
List<SimpleGrantedAuthority> authorityList = new ArrayList<>();
authorityList.add(new SimpleGrantedAuthority("ROLE_user"));
// 这里只是模拟,实际可以与数据库进行交互获得用户权限
UserDetails user = new User(username,pwdCrypt,authorityList);
return user;
}
}
配置
WebSecurityConfigurerAdapter
类
// 注入创建的 UserService
@Bean
public MyUserDetailsService myUserDetailsService(){
return new MyUserDetailsService();
}
// 重写 Configure 方法,使配置生效
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService()) // 注入 UserDetailsService
.passwordEncoder(passwordEncoder());
}
// 将代理 AuthenticationManager 注册成 Bean,供直接使用
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
Ps: 一般来说
UserDetails
一般是由UserDetailsService
来返回的。DaoAuthenticationProvider
验证UserDetails
并返回一个Authentication
对象
AuthenticationEntryPoint
他所建模的概念是:“认证入口点”。它在用户请求处理过程中遇到认证异常时,被ExceptionTranslationFilter用于开启特定认证方案(authentication schema)的认证流程。
这为登陆失败时的流程:
ExceptionTranslationFilter
调用 FilterChain.doFilter(request, response)
来调用应用程序的其余部分
AuthenticationEntryPoint
接口
创建一个 MyAuthenticationEntryPoint
类,并实现 AuthenticationEntryPoint
接口
@Component
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); // 自定义处理返回信息
}
}
配置
WebSecurityConfigurerAdapter
类
@Autowired
MyAuthenticationEntryPoint authenticationEntryPoint; // 注入自己实现的 EntryPoint
// Step2: 重写 configure 方法
@Override
public void configure(HttpSecurity http){
try {
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
} catch (Exception e) {
e.printStackTrace();
}
}
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有