目录
6、 如何在 Spring Security 中自定义认证逻辑?
7、 如何在 Spring Security 中使用基于角色的访问控制?
9、什么是 OAuth2,Spring Security 如何支持 OAuth2?
10、 如何在 Spring Security 中实现 JWT 认证?
11、如何配置基本的HTTP基本认证(Basic Authentication)?
12、如何配置表单登录认证(Form-based Authentication)?
13、如何使用JWT(JSON Web Token)实现无状态的身份验证?
14、如何在Spring Security中配置记住我(Remember-Me)功能?
15、如何在Spring Security中实现自定义的访问控制?
16、hasRole 和 hasAuthority 有区别吗?
18、SpringSecurity如何优雅更换系统使用的加密算法?
1、什么是 Spring Security?
答:Spring Security 是一个用于 Java 应用程序的安全框架,它提供了认证、授权和安全防护等功能。它的主要目标是提供一个简单且可扩展的安全解决方案,使开发人员可以轻松地将安全功能集成到应用程序中。
2、 Spring Security 提供了哪些核心功能?
答:Spring Security 提供了以下核心功能:
- 认证(Authentication):验证用户身份的过程。
- 授权(Authorization):确定用户是否具有访问特定资源的权限。
- 防护攻击(Protection against attacks):保护应用程序免受常见安全攻击,如跨站请求伪造(CSRF)和跨站脚本攻击(XSS)。
- Servlet API 集成:与 Java Servlet API 无缝集成,提供 Web 安全功能。
- 可扩展性:通过自定义组件和扩展点,可以轻松地扩展 Spring Security。
3、认证和授权有什么区别?
答: 认证意味着确认自己的身份,而授权意味着授予对系统的访问权限。简单来说,认证是验证身份的过程,而授权是验证有权访问的过程。
- 认证(Authentication):是验证用户身份的过程,通常涉及到用户名和密码的检查。成功认证后,用户将获得一个认证令牌,用于后续的授权过程。
- 授权(Authorization):是确定用户是否具有访问特定资源或执行特定操作的权限的过程。系统基于用户的角色、权限或访问控制列表(ACL)来判断用户是否具有访问权限。
4、请简要介绍 Spring Security 的架构。
答:Spring Security 的架构主要由以下组件组成:
SecurityContextHolder
:存储与当前线程关联的安全上下文。Authentication
:表示用户的认证信息。UserDetails
:表示用户的详细信息。UserDetailsService
:用于加载用户详细信息的接口。AuthenticationManager
:负责处理认证请求。AccessDecisionManager
:负责授权决策。FilterChainProxy
:负责处理 HTTP 请求的过滤器链。SecurityFilterChain
:由一系列安全过滤器组成的链。
5、什么是 SecuritycContextHolder?
答:SecurityContextHolder
是一个用于存储与当前线程关联的安全上下文的类。它使用 ThreadLocal
机制来存储 SecurityContext
。SecurityContext
包含了当前用户的认证信息,如 Authentication
对象。
6、 如何在 Spring Security 中自定义认证逻辑?
答:要在 Spring Security 中自定义认证逻辑,需要实现 AuthenticationProvider
接口,并在 configure()
方法中将自定义的 AuthenticationProvider
添加到 AuthenticationManagerBuilder
:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(customAuthenticationProvider);
}
}
7、 如何在 Spring Security 中使用基于角色的访问控制?
答:要在 Spring Security 中使用基于角色的访问控制,可以在 configure()
方法中配置 HttpSecurity
:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasRole("USER")
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();
}
在这个示例中,我们配置了两个基于角色的访问控制规则:
/admin/**
路径只允许具有ADMIN
角色的用户访问。/user/**
路径只允许具有USER
角色的用户访问。
8、如何防止跨站请求伪造(CSRF)攻击?
答:Spring Security 默认启用了 CSRF 防护。要禁用 CSRF 防护,可以在 configure()
方法中配置 HttpSecurity
:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();
}
}
但是,通常不推荐禁用 CSRF 防护。如果确实需要禁用 CSRF 防护,请确保您了解可能带来的安全风险。
9、什么是 OAuth2,Spring Security 如何支持 OAuth2?
答:OAuth2 是一个开放标准,用于实现安全的 API 访问授权。它允许用户向第三方应用程序授权访问其资源,而无需将用户名和密码提供给第三方应用程序。
Spring Security 支持 OAuth2 通过 spring-security-oauth2
模块。要在 Spring Security 中使用 OAuth2,需要引入相关依赖并进行相应的配置。主要有以下几个方面:
- 配置 OAuth2 服务器:通过
@EnableAuthorizationServer
注解启用 OAuth2 授权服务器。 - 配置 OAuth2 客户端:通过
@EnableOAuth2Client
注解启用 OAuth2 客户端。 - 配置 OAuth2 资源服务器:通过
@EnableResourceServer
注解启用 OAuth2 资源服务器。
10、 如何在 Spring Security 中实现 JWT 认证?
答:要在 Spring Security 中实现基于 JWT(JSON Web Token)的认证,需要执行以下步骤:
- 引入 JWT 相关依赖,如
jjwt
。 - 实现一个用于生成和解析 JWT 的工具类。
- 创建一个自定义的
AuthenticationFilter
,用于从请求头中提取 JWT 并进行认证。 - 在
SecurityConfig
类中配置HttpSecurity
,将自定义的AuthenticationFilter
添加到过滤器链。
以下是一个简单的示例:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.anyRequest().authenticated();
}
}
在这个示例中,我们禁用了 CSRF 防护和会话管理,然后将自定义的 JwtAuthenticationFilter
添加到过滤器链。
11、如何配置基本的HTTP基本认证(Basic Authentication)?
答:可以使用Spring Security配置基本的HTTP基本认证。
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("{noop}password").roles("USER");
}
}
12、如何配置表单登录认证(Form-based Authentication)?
答:可以使用Spring Security配置表单登录认证
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.permitAll()
.and()
.logout()
.permitAll();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("{noop}password").roles("USER");
}
}
13、如何使用JWT(JSON Web Token)实现无状态的身份验证?
答:可以使用Spring Security和JWT实现无状态的身份验证。
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtTokenProvider jwtTokenProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.apply(new JwtConfigurer(jwtTokenProvider));
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(new JwtAuthenticationProvider());
}
}
14、如何在Spring Security中配置记住我(Remember-Me)功能?
答:可以使用Spring Security配置记住我功能。
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.and()
.rememberMe();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("{noop}password").roles("USER");
}
}
15、如何在Spring Security中实现自定义的访问控制?
答:可以使用@PreAuthorize
和@PostAuthorize
注解或实现AccessDecisionVoter
接口来实现自定义的访问控制。
@Service
public class CustomPermissionEvaluator implements PermissionEvaluator {
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
// Custom permission checking logic
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
// Custom permission checking logic
}
}
16、hasRole 和 hasAuthority 有区别吗?
答:在Spring Security中,hasRole和hasAuthority都可以用来控制用户的访问权限,但它们有一些细微的差别。
hasRole方法是基于角色进行访问控制的。它检查用户是否有指定的角色,并且这些角色以"ROLE_"前缀作为前缀(例如"ROLE_ADMIN")。
hasAuthority方法是基于权限进行访问控制的。它检查用户是否有指定的权限,并且这些权限没有前缀。
因此,使用hasRole方法需要在用户的角色名称前添加"ROLE_"前缀,而使用hasAuthority方法不需要这样做。
例如,假设用户有一个角色为"ADMIN"和一个权限为"VIEW_REPORTS",可以使用以下方式控制用户对页面的访问权限:
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/reports/**").hasAuthority("VIEW_REPORTS")
在这个例子中,只有具有"ROLE_ADMIN"角色的用户才能访问/admin/路径下的页面,而具有"VIEW_REPORTS"权限的用户才能访问/reports/路径下的页面。
17、如何对密码进行加密?
答:在 Spring Security 中对密码进行加密通常使用的是密码编码器(PasswordEncoder)。PasswordEncoder 的作用是将明文密码加密成密文密码,以便于存储和校验。Spring Security 提供了多种常见的密码编码器,例如 BCryptPasswordEncoder、SCryptPasswordEncoder、StandardPasswordEncoder 等。
以 BCryptPasswordEncoder 为例,使用步骤如下:
1.在 pom.xml 文件中添加 BCryptPasswordEncoder 的依赖:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
<version>5.6.1</version>
</dependency>
2.在 Spring 配置文件中注入 BCryptPasswordEncoder:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// ...
}
3.在使用密码的地方调用 passwordEncoder.encode()
方法对密码进行加密,例如注册时对密码进行加密:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public User register(User user) {
String encodedPassword = passwordEncoder.encode(user.getPassword());
user.setPassword(encodedPassword);
// ...
return user;
}
// ...
}
以上就是使用 BCryptPasswordEncoder 对密码进行加密的步骤。使用其他密码编码器的步骤类似,只需将 BCryptPasswordEncoder 替换为相应的密码编码器即可。
18、SpringSecurity如何优雅更换系统使用的加密算法?
答:推荐的做法是通过 DelegatingPasswordEncoder 兼容多种不同的密码加密方案,以适应不同的业务需求。
DelegatingPasswordEncoder 其实就是一个代理类,并非是一种全新的加密算法,它做的事情就是代理上面提到的加密算法实现类。在 Spring Security 5.0 之后,默认就是基于 DelegatingPasswordEncoder 进行密码加密的。
19、SpringSecurity如何解决跨域问题?
答:SpringSecurity中提供了专业的方式来解决预检请求所面临的问题:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
// 开启跨域配置
.cors()
.configurationSource(corsConfigurationSource())
.and()
.csrf().disable();
}
CorsConfigurationSource corsConfigurationSource() {
// 提供CorsConfiguration实例,并配置跨域信息
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
corsConfiguration.setAllowedMethods(Arrays.asList("*"));
corsConfiguration.setAllowedOrigins(Arrays.asList("http://localhost:8081"));
corsConfiguration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfiguration);
return source;
}
}
cors()方法开启了对CorsConfigurer的配置,其最重要的方法就是configure方法:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
// 开启跨域配置
.cors()
.configurationSource(corsConfigurationSource())
.and()
.csrf().disable();
}
CorsConfigurationSource corsConfigurationSource() {
// 提供CorsConfiguration实例,并配置跨域信息
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
corsConfiguration.setAllowedMethods(Arrays.asList("*"));
corsConfiguration.setAllowedOrigins(Arrays.asList("http://localhost:8081"));
corsConfiguration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfiguration);
return source;
}
}
拿到CorsFilter之后,调用http.addFilter方法将其添加到spring security过滤器链中,在过滤器链构建之前,会先对所有的过滤器进行排序,排序的依据在FilterOrderRegistration中已经定义好了:
FilterOrderRegistration() {
Step order = new Step(INITIAL_ORDER, ORDER_STEP);
put(ChannelProcessingFilter.class, order.next());
order.next(); // gh-8105
put(WebAsyncManagerIntegrationFilter.class, order.next());
put(SecurityContextPersistenceFilter.class, order.next());
put(HeaderWriterFilter.class, order.next());
put(CorsFilter.class, order.next());
put(CsrfFilter.class, order.next());
put(LogoutFilter.class, order.next());
// ...
}
可以看到,CorsFilter的位置在HeaderWriterFilter之后,在CsrfFilter之前,这个时候还没到认证过滤器。Spring security根据开发者提供的CorsConfigurationSource对象构建出一个CorsFilter,并将该过滤器置于认证过滤器之前。
20、SpringSecurity执行流程?
答:1.客户端发起一个请求,进入 Security 过滤器链。
2.当到 LogoutFilter 的时候判断是否是登出路径,如果是登出路径则到 logoutHandler ,如果登出成功则到 logoutSuccessHandler 登出成功处理,如果登出失败则由 ExceptionTranslationFilter ;如果不是登出路径则直接进入下一个过滤器。
3.当到 UsernamePasswordAuthenticationFilter 的时候判断是否为登录路径,如果是,则进入该过滤器进行登录操作,如果登录失败则到 AuthenticationFailureHandler 登录失败处理器处理,如果登录成功则到 AuthenticationSuccessHandler 登录成功处理器处理,如果不是登录请求则不进入该过滤器。
4.当到 FilterSecurityInterceptor 的时候会拿到 uri ,根据 uri 去找对应的鉴权管理器,鉴权管理器做鉴权工作,鉴权成功则到 Controller 层否则到 AccessDeniedHandler 鉴权失败处理器处理。