Skip to content

Spring Security面试题

1. Spring Security的基本概念

问题:什么是Spring Security?

答案

  • Spring Security:基于Spring框架的安全框架,用于保护Spring应用程序。
  • 特点
    • 认证
    • 授权
    • 防护攻击
    • 集成Spring
    • 高度可定制

2. 认证和授权

问题:认证和授权有什么区别?

答案

  • 认证:验证用户的身份,确认用户是谁。
  • 授权:验证用户的权限,确认用户能做什么。

3. Spring Security的架构

问题:Spring Security的架构是怎样的?

答案

Spring Security架构
├── 认证
│   ├── 认证管理器
│   ├── 认证提供者
│   └── 用户详情服务
├── 授权
│   ├── 访问决策管理器
│   ├── 投票者
│   └── 安全拦截器
└── 防护攻击
    ├── CSRF
    ├── XSS
    ├── 点击劫持
    └── 会话固定

4. SecurityFilterChain

问题:SecurityFilterChain的作用是什么?

答案

  • SecurityFilterChain:Spring Security的核心组件,包含一系列过滤器。
  • 作用
    • 处理HTTP请求
    • 执行认证和授权
    • 防护攻击

示例

java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/**").permitAll()
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .permitAll()
            )
            .logout(logout -> logout
                .permitAll()
            );
        return http.build();
    }
}

5. UserDetailsService

问题:UserDetailsService的作用是什么?

答案

  • UserDetailsService:用于加载用户信息。
  • 作用
    • 根据用户名加载用户信息
    • 返回UserDetails对象

示例

java
@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired
    private UserRepository userRepository;
    
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("User not found");
        }
        return new CustomUserDetails(user);
    }
}

6. UserDetails

问题:UserDetails的作用是什么?

答案

  • UserDetails:表示用户信息。
  • 作用
    • 存储用户名
    • 存储密码
    • 存储权限
    • 存储账户状态

示例

java
public class CustomUserDetails implements UserDetails {
    private User user;
    
    public CustomUserDetails(User user) {
        this.user = user;
    }
    
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return user.getRoles().stream()
            .map(role -> new SimpleGrantedAuthority(role.getName()))
            .collect(Collectors.toList());
    }
    
    @Override
    public String getPassword() {
        return user.getPassword();
    }
    
    @Override
    public String getUsername() {
        return user.getUsername();
    }
    
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    
    @Override
    public boolean isEnabled() {
        return user.isEnabled();
    }
}

7. PasswordEncoder

问题:PasswordEncoder的作用是什么?

答案

  • PasswordEncoder:用于密码加密和验证。
  • 实现
    • BCryptPasswordEncoder
    • Pbkdf2PasswordEncoder
    • SCryptPasswordEncoder
    • Argon2PasswordEncoder

示例

java
@Configuration
public class SecurityConfig {
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

// 使用
@Autowired
private PasswordEncoder passwordEncoder;

public void createUser(User user) {
    String encodedPassword = passwordEncoder.encode(user.getPassword());
    user.setPassword(encodedPassword);
    userRepository.save(user);
}

8. 认证方式

问题:Spring Security支持哪些认证方式?

答案

  • 表单登录:使用表单进行认证。
  • HTTP Basic:使用HTTP Basic认证。
  • HTTP Digest:使用HTTP Digest认证。
  • X.509:使用证书认证。
  • LDAP:使用LDAP认证。
  • OAuth2:使用OAuth2认证。
  • JWT:使用JWT认证。

9. 表单登录

问题:如何配置表单登录?

答案

java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .formLogin(form -> form
                .loginPage("/login")
                .loginProcessingUrl("/login")
                .defaultSuccessUrl("/home")
                .failureUrl("/login?error=true")
                .usernameParameter("username")
                .passwordParameter("password")
                .permitAll()
            );
        return http.build();
    }
}

10. HTTP Basic认证

问题:如何配置HTTP Basic认证?

答案

java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .httpBasic(basic -> basic
                .realmName("My Realm")
            );
        return http.build();
    }
}

11. JWT认证

问题:如何实现JWT认证?

答案

java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            )
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/auth/**").permitAll()
                .anyRequest().authenticated()
            )
            .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
}

// JWT过滤器
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Autowired
    private JwtTokenProvider jwtTokenProvider;
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String token = jwtTokenProvider.resolveToken(request);
        if (token != null && jwtTokenProvider.validateToken(token)) {
            Authentication auth = jwtTokenProvider.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(auth);
        }
        filterChain.doFilter(request, response);
    }
}

12. OAuth2

问题:什么是OAuth2?

答案

  • OAuth2:授权框架,用于第三方应用访问用户资源。
  • 角色
    • 资源拥有者
    • 客户端
    • 授权服务器
    • 资源服务器
  • 授权模式
    • 授权码模式
    • 简化模式
    • 密码模式
    • 客户端模式

13. 授权方式

问题:Spring Security支持哪些授权方式?

答案

  • 基于角色的授权:使用hasRole、hasAnyRole。
  • 基于权限的授权:使用hasAuthority、hasAnyAuthority。
  • 基于表达式的授权:使用@PreAuthorize、@PostAuthorize。
  • 基于注解的授权:使用@Secured、@RolesAllowed。

示例

java
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .requestMatchers("/user/**").hasAnyRole("ADMIN", "USER")
                .requestMatchers("/api/**").hasAuthority("API_ACCESS")
                .anyRequest().authenticated()
            );
        return http.build();
    }
}

// 使用注解
@PreAuthorize("hasRole('ADMIN')")
public void adminMethod() {
}

@PreAuthorize("hasAuthority('API_ACCESS')")
public void apiMethod() {
}

14. CSRF防护

问题:什么是CSRF?如何防护?

答案

  • CSRF(Cross-Site Request Forgery):跨站请求伪造,攻击者诱导用户执行非预期的操作。
  • 防护方式
    • 使用CSRF Token
    • 验证Referer头
    • 使用SameSite Cookie

示例

java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
            );
        return http.build();
    }
}

15. CORS

问题:什么是CORS?如何配置?

答案

  • CORS(Cross-Origin Resource Sharing):跨域资源共享,允许浏览器跨域访问资源。
  • 配置方式

示例

java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .cors(cors -> cors.configurationSource(corsConfigurationSource()));
        return http.build();
    }
    
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("*"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setAllowCredentials(true);
        
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

16. 会话管理

问题:Spring Security如何管理会话?

答案

  • 会话创建策略
    • ALWAYS:总是创建会话。
    • NEVER:不创建会话,如果需要会话则抛出异常。
    • IF_REQUIRED:需要时创建会话。
    • STATELESS:不创建会话,适用于JWT认证。
  • 会话固定防护:防止会话固定攻击。

示例

java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .sessionFixation().migrateSession()
                .maximumSessions(1)
                .maxSessionsPreventsLogin(true)
            );
        return http.build();
    }
}

17. 记住我

问题:如何实现记住我功能?

答案

java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .rememberMe(remember -> remember
                .key("uniqueAndSecret")
                .tokenValiditySeconds(86400)
                .userDetailsService(userDetailsService)
                .rememberMeCookieName("remember-me")
            );
        return http.build();
    }
}

18. 匿名访问

问题:如何配置匿名访问?

答案

java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .anonymous(anonymous -> anonymous
                .key("anonymousKey")
                .authorities(new SimpleGrantedAuthority("ROLE_ANONYMOUS"))
            )
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
            );
        return http.build();
    }
}

19. 自定义过滤器

问题:如何添加自定义过滤器?

答案

java
@Component
public class CustomFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        System.out.println("Custom Filter");
        filterChain.doFilter(request, response);
    }
}

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Autowired
    private CustomFilter customFilter;
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
}

20. 自定义认证成功/失败处理

问题:如何自定义认证成功/失败处理?

答案

java
@Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        response.sendRedirect("/home");
    }
}

@Component
public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
        response.sendRedirect("/login?error=true");
    }
}

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Autowired
    private CustomAuthenticationSuccessHandler successHandler;
    
    @Autowired
    private CustomAuthenticationFailureHandler failureHandler;
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .formLogin(form -> form
                .successHandler(successHandler)
                .failureHandler(failureHandler)
            );
        return http.build();
    }
}

21. 方法级安全

问题:如何实现方法级安全?

答案

java
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
}

@Service
public class UserService {
    @PreAuthorize("hasRole('ADMIN')")
    public void adminMethod() {
    }
    
    @PreAuthorize("hasAuthority('USER_READ')")
    public User readUser() {
        return new User();
    }
    
    @PreAuthorize("#userId == authentication.principal.id")
    public User getUserById(int userId) {
        return new User();
    }
    
    @PostFilter("filterObject.owner == authentication.principal.username")
    public List<Document> getDocuments() {
        return new ArrayList<>();
    }
    
    @PreFilter("filterObject.owner == authentication.principal.username")
    public void saveDocuments(List<Document> documents) {
    }
}

22. 动态权限

问题:如何实现动态权限?

答案

java
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
    @Autowired
    private PermissionService permissionService;
    
    @Override
    public boolean hasPermission(Authentication authentication, Object targetId, Object permission) {
        return permissionService.hasPermission(authentication, targetId, permission);
    }
    
    @Override
    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
        return permissionService.hasPermission(authentication, targetId, targetType, permission);
    }
}

@Configuration
@EnableMethodSecurity
public class SecurityConfig {
    @Bean
    public MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
        DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
        handler.setPermissionEvaluator(new CustomPermissionEvaluator());
        return handler;
    }
}

23. 多租户

问题:如何在Spring Security中实现多租户?

答案

java
@Component
public class TenantFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String tenantId = request.getHeader("X-Tenant-ID");
        TenantContext.setTenantId(tenantId);
        try {
            filterChain.doFilter(request, response);
        } finally {
            TenantContext.clear();
        }
    }
}

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Autowired
    private TenantFilter tenantFilter;
    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .addFilterBefore(tenantFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
}

24. 社交登录

问题:如何实现社交登录?

答案

java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .oauth2Login(oauth2 -> oauth2
                .loginPage("/login")
                .userInfoEndpoint(userInfo -> userInfo
                    .userService(customOAuth2UserService)
                )
            );
        return http.build();
    }
}

@Service
public class CustomOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2User oauth2User = delegate.loadUser(userRequest);
        return processOAuth2User(oauth2User);
    }
}

25. Spring Security的最佳实践

问题:使用Spring Security的最佳实践有哪些?

答案

  • 使用强密码加密算法,如BCrypt。
  • 使用HTTPS保护敏感数据。
  • 使用CSRF Token防护CSRF攻击。
  • 使用CORS配置跨域访问。
  • 使用会话管理防止会话固定攻击。
  • 使用方法级安全保护方法。
  • 使用自定义过滤器实现自定义逻辑。
  • 使用动态权限实现灵活的权限控制。
  • 使用OAuth2实现第三方登录。
  • 使用JWT实现无状态认证。