20道SpringSecurity常见面试题汇总(超详细回答 !!!)

目录

1、什么是 Spring Security?

2、 Spring Security 提供了哪些核心功能?

3、认证和授权有什么区别?

4、请简要介绍 Spring Security 的架构。

5、什么是 SecuritycContextHolder?

6、 如何在 Spring Security 中自定义认证逻辑?

7、 如何在 Spring Security 中使用基于角色的访问控制?

8、如何防止跨站请求伪造(CSRF)攻击?

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 有区别吗?

17、如何对密码进行加密?

18、SpringSecurity如何优雅更换系统使用的加密算法?

19、SpringSecurity如何解决跨域问题?

20、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 机制来存储 SecurityContextSecurityContext 包含了当前用户的认证信息,如 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)的认证,需要执行以下步骤:

  1. 引入 JWT 相关依赖,如 jjwt
  2. 实现一个用于生成和解析 JWT 的工具类。
  3. 创建一个自定义的 AuthenticationFilter,用于从请求头中提取 JWT 并进行认证。
  4. 在 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 鉴权失败处理器处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值