17个SpringSecurity业务场景案例,全面掌握安全框架(实战篇)

158 篇文章 0 订阅
88 篇文章 0 订阅

在这里插入图片描述

Spring Security 通过一系列注解简化了安全配置,我们将深入探讨Spring Security框架的17个关键应用场景,包括认证、授权、OAuth2、CSRF保护等。每个案例都配有详细的时序图和代码示例,旨在帮助开发者全面理解并有效利用Spring Security的强大功能,以构建更安全、更可靠的应用程序。

肖哥弹架构 跟大家“弹弹” 关注公号回复 ‘mvcc’ 获得手写数据库事务代码

欢迎 点赞,关注,评论。

关注公号Solomon肖哥弹架构获取更多精彩内容

历史热点文章

Spring Security 常用应用场景介绍:

  1. 认证(Authentication)
    • 应用场景:确保只有经过验证的用户才能访问应用程序。
  2. 授权(Authorization)
    • 应用场景:控制用户对特定资源的访问权限,如角色基础的访问控制。
  3. 表单登录(Form-Based Login)
    • 应用场景:为用户提供登录表单,处理登录请求和重定向。
  4. HTTP 基本认证(HTTP Basic Authentication)
    • 应用场景:为 RESTful API 或其他服务提供基础的用户名和密码认证。
  5. OAuth2 和 OpenID Connect
    • 应用场景:支持现代的授权框架,适用于需要第三方应用认证的场景。
  6. CSRF 保护(CSRF Protection)
    • 应用场景:防止跨站请求伪造攻击,保护 Web 应用程序的安全。
  7. 密码编码(Password Encoding)
    • 应用场景:安全地存储用户密码,防止密码泄露。
  8. 方法级安全性(Method Security)
    • 应用场景:控制对特定方法或 Bean 属性的访问,实现细粒度的安全控制。
  9. 异常处理(Exception Handling)
    • 应用场景:自定义安全相关的异常处理,如认证失败、授权失败。
  10. 记住我(Remember-Me)
    • 应用场景:为用户提供持久的登录状态,方便用户再次访问。
  11. 预授权(Pre-Invocation)
    • 应用场景:在方法执行前进行安全检查,确保方法调用的安全性。
  12. 表达式支持(Expression-Based)
    • 应用场景:使用 Spring Expression Language (SpEL) 实现复杂的安全逻辑。
  13. 安全上下文(Security Context)
    • 应用场景:管理和检索认证信息,如获取当前认证用户。
  14. 安全过滤器链(Security Filter Chain)
    • 应用场景:处理 HTTP 请求的安全检查,如认证、授权。
  15. 用户详细信息服务(UserDetailsService)
    • 应用场景:自定义用户认证信息的加载逻辑,如从数据库加载用户数据。
  16. 多因素认证(Multi-Factor Authentication)
    • 应用场景:增加额外的安全层,如短信验证码、电子邮件确认。
  17. 匿名访问(Anonymous Access)
    • 应用场景:允许未认证的用户访问某些公共资源。

Spring Security 场景案例

1、认证(Authentication)

业务场景: 一个在线书店系统,用户需要登录后才能查看订单和购买书籍。

业务时序图

在这里插入图片描述

在这里插入图片描述

  1. 用户(User) 访问登录页面。
  2. 表单登录认证过滤器(FormLoginAuthenticationFilter) 显示登录表单。
  3. 用户填写凭据并提交。
  4. 表单登录认证过滤器(FormLoginAuthenticationFilter) 调用 认证管理器(AuthenticationManager)attemptAuthentication 方法。
  5. 认证管理器(AuthenticationManager) 请求 用户详细信息服务(UserDetailsService) 根据用户名加载用户信息。
  6. 用户详细信息服务(UserDetailsService) 调用自定义的 CustomUserDetailsService 获取用户详细信息。
  7. CustomUserDetailsService 返回用户详细信息给 认证管理器(AuthenticationManager)
  8. 认证管理器(AuthenticationManager) 请求 密码编码器(PasswordEncoder) 对用户输入的密码进行编码。
  9. 密码编码器(PasswordEncoder) 将编码后的密码与存储的密码进行比较。
  10. 认证管理器(AuthenticationManager) 根据比较结果决定认证是否成功。
  11. 表单登录认证过滤器(FormLoginAuthenticationFilter) 根据认证结果重定向用户到主页或显示错误。
  12. 认证信息被设置到 SecurityContextHolder 中。
Spring Security 配置

首先,我们需要配置Spring Security来处理用户的登录请求。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/login", "/register").permitAll()
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/home", true)
                .permitAll()
            .and()
            .logout()
                .permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
            .withUser("user").password(passwordEncoder().encode("password")).roles("USER")
            .and()
            .withUser("admin").password(passwordEncoder().encode("admin")).roles("ADMIN");
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
业务逻辑代码

接下来,我们创建一个服务来处理用户的登录逻辑。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

@Service
public class AuthService {

    @Autowired
    private AuthenticationManager authenticationManager;

    /**
     * 用户登录方法
     * 
     * @param username 用户名
     * @param password 密码
     * @return 登录成功返回 true,否则返回 false
     */
    public boolean authenticateUser(String username, String password) {
        try {
            // 调用 Spring Security 的认证管理器进行认证
            Authentication authentication = authenticationManager
                .authenticate(new UsernamePasswordAuthenticationToken(username, password));
            
            // 将认证信息放入安全上下文中
            authenticationManager.authenticate(authentication);

            // 认证成功
            return true;
        } catch (Exception e) {
            // 认证失败
            return false;
        }
    }
}
登录控制器

最后,我们需要一个控制器来处理用户的登录请求。

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class LoginController {

    @GetMapping("/login")
    public String login() {
        return "login";
    }

    @GetMapping("/home")
    public String home() {
        return "home";
    }
}
登录页面 (login.html)
<!DOCTYPE html>
<html>
<head>
    <title>Login Page</title>
</head>
<body>
    <form action="/login" method="post">
        <div>
            <label>Username: <input type="text" name="username"/></label>
        </div>
        <div>
            <label>Password: <input type="password" name="password"/></label>
        </div>
        <div>
            <button type="submit">Login</button>
        </div>
    </form>
</body>
</html>

2、授权(Authorization)

业务场景: 一个企业资源规划(ERP)系统,其中不同用户根据其角色拥有不同的访问权限。例如,只有财务部门的员工才能访问薪资信息。

业务时序图

在这里插入图片描述

  1. 用户(User) 请求受保护的资源。
  2. 过滤器链代理(FilterChainProxy) 处理安全过滤器链。
  3. 认证管理器(AuthenticationManager) 验证用户身份。
  4. 安全上下文持有器(SecurityContextHolder) 加载认证信息。
  5. 自定义用户详细信息服务(CustomUserDetailsService) 从数据库或其他存储中加载用户的权限和角色。
  6. 认证管理器(AuthenticationManager) 将用户的权限和角色返回给安全上下文。
  7. 安全过滤器链(FilterChainProxy) 检查是否有方法级安全性注解,如 @PreAuthorize@Secured
  8. 方法安全拦截器(MethodSecurityInterceptor) 检查注解并评估表达式。
  9. 基于表达式的访问控制列表(ExpressionBasedAccessControlList) 根据定义的SpEL表达式计算访问决策。
  10. 方法安全拦截器(MethodSecurityInterceptor) 根据访问决策允许或拒绝访问。
  11. 过滤器链代理(FilterChainProxy) 根据方法安全拦截器的结果,授予用户访问权限或返回403错误。

Spring Security 配置

首先,我们需要配置Spring Security来处理授权请求。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/hr/**").hasRole("HR")
                .antMatchers("/finance/**").hasAuthority("FINANCE_READ")
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/home", true)
            .and()
            .logout()
                .permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
            .passwordEncoder(passwordEncoder())
            .withUser("admin").password(passwordEncoder().encode("admin")).roles("ADMIN")
            .and()
            .withUser("hr").password(passwordEncoder().encode("hr")).roles("HR")
            .and()
            .withUser("finance").password(passwordEncoder().encode("finance")).authorities("FINANCE_READ");
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
业务逻辑代码

接下来,我们创建一个服务来处理访问薪资信息的授权逻辑。

import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.stereotype.Service;

@Service
public class FinanceService {

    /**
     * 获取部门薪资信息
     * 
     * @param departmentId 部门ID
     * @return 薪资信息
     */
    @PostAuthorize("returnObject != null and returnObject.departmentId == authentication.principal.departmentId")
    public Salary getDepartmentSalary(int departmentId) {
        // 模拟从数据库获取薪资信息
        return new Salary(departmentId, 50000);
    }
}

class Salary {
    private int departmentId;
    private double amount;

    public Salary(int departmentId, double amount) {
        this.departmentId = departmentId;
        this.amount = amount;
    }

    public int getDepartmentId() {
        return departmentId;
    }

    public double getAmount() {
        return amount;
    }
}
控制器

最后,我们需要一个控制器来处理用户的请求。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class FinanceController {

    @Autowired
    private FinanceService financeService;

    @GetMapping("/finance/{departmentId}/salary")
    public Salary getSalary(@PathVariable int departmentId) {
        return financeService.getDepartmentSalary(departmentId);
    }
}

3、表单登录(Form-Based Login)

业务场景: 一个在线图书馆系统,用户需要通过填写登录表单来访问他们的借阅历史和个人信息。

业务时序图

在这里插入图片描述

  1. 用户(User) 访问登录页面。
  2. 表单登录认证过滤器(FormLoginAuthenticationFilter) 显示登录表单。
  3. 用户填写凭据并提交。
  4. 表单登录认证过滤器(FormLoginAuthenticationFilter) 调用 认证管理器(AuthenticationManager)attemptAuthentication 方法。
  5. 认证管理器(AuthenticationManager) 请求 用户详细信息服务(UserDetailsService) 根据用户名加载用户信息。
  6. 用户详细信息服务(UserDetailsService) 调用 密码编码器(PasswordEncoder) 对数据库中的密码进行编码。
  7. 密码编码器(PasswordEncoder) 将编码后的密码返回给认证管理器。
  8. 认证管理器(AuthenticationManager) 根据比较结果决定认证是否成功。
  9. 表单登录认证过滤器(FormLoginAuthenticationFilter) 根据认证结果设置 安全上下文持有器(SecurityContextHolder)
  10. 安全上下文持有器(SecurityContextHolder) 根据认证结果重定向用户到主页或显示错误。
Spring Security 配置

首先,我们需要配置Spring Security来处理表单登录请求。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home", "/register").permitAll()
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/home", true)
                .permitAll()
            .and()
            .logout()
                .permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
            .passwordEncoder(passwordEncoder())
            .withUser("user").password(passwordEncoder().encode("password")).roles("USER")
            .and()
            .withUser("admin").password(passwordEncoder().encode("admin")).roles("ADMIN");
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
登录控制器

接下来,我们创建一个控制器来处理登录页面的请求。

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class LoginController {

    @GetMapping("/login")
    public String login() {
        return "login";
    }

    @GetMapping("/home")
    public String home() {
        return "home";
    }
}
登录页面 (login.html)
<!DOCTYPE html>
<html>
<head>
    <title>Login</title>
</head>
<body>
    <h2>Login</h2>
    <form action="/login" method="post">
        <div>
            <label>Username: <input type="text" name="username"/></label>
        </div>
        <div>
            <label>Password: <input type="password" name="password"/></label>
        </div>
        <div>
            <button type="submit">Login</button>
        </div>
    </form>
</body>
</html>
Home页面 (home.html)
<!DOCTYPE html>
<html>
<head>
    <title>Home</title>
</head>
<body>
    <h1>Welcome to the Library System</h1>
    <p>Access your borrowing history and personal information.</p>
</body>
</html>
配置静态资源

确保Spring Boot项目能够正确地服务静态资源,如HTML页面。

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MvcConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/");
    }
}

4、HTTP 基本认证(HTTP Basic Authentication)

业务场景: 一个内部API服务,用于提供员工薪资信息。只有经过身份验证的员工才能访问此API。

业务时序图

在这里插入图片描述

  1. 用户(User) 在浏览器(Browser)中输入需要访问的 URL。
  2. 浏览器(Browser) 发送 HTTP 请求到服务器(Server)。
  3. 服务器(Server) 通过 HttpBasicAuthenticationFilter 发起 HTTP 基本认证挑战。
  4. HttpBasicAuthenticationFilter 向用户提示输入凭据。
  5. 用户(User) 在浏览器中输入用户名和密码。
  6. 浏览器(Browser) 在随后的请求中将凭据发送到服务器,包含在 HTTP 头的 Authorization 字段中。
  7. HttpBasicAuthenticationFilter 调用 AuthenticationManager 进行认证。
  8. AuthenticationManager 请求 UserDetailsService 根据用户名加载用户信息。
  9. UserDetailsService 调用 PasswordEncoder 对数据库中的密码进行编码。
  10. PasswordEncoder 将编码后的密码返回给认证管理器。
  11. AuthenticationManager 根据比较结果决定认证是否成功。
  12. HttpBasicAuthenticationFilter 根据认证结果向服务器发送响应,可能是 200 OK 或 401 Unauthorized。
  13. 服务器(Server) 将响应返回给浏览器,浏览器显示响应内容或显示错误信息。
Spring Security 配置

首先,我们需要配置Spring Security以启用HTTP基本认证。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.config.http.SessionCreationPolicy;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/api/salaries/**").authenticated()
                .anyRequest().permitAll()
            .and()
            .httpBasic()  // 启用HTTP基本认证
            .and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);  // 由于是REST API,不需要session

        http.csrf().disable();  // 禁用CSRF保护
    }

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        // 这里使用内存中的用户详情服务,生产环境应该使用数据库或其他服务
        return new InMemoryUserDetailsManager(
            User.withUsername("employee").password("{noop}password").authorities("SALARY_READ").build()
        );
    }
}
业务逻辑代码

创建一个控制器来处理薪资信息的请求。

import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class SalaryController {

    @GetMapping("/salaries")
    @PreAuthorize("hasAuthority('SALARY_READ')")
    public ResponseEntity<Double> getSalary() {
        // 这里应该包含从数据库或其他存储获取薪资信息的逻辑
        double salary = 50000;  // 模拟薪资数据
        return ResponseEntity.ok(salary);
    }
}
测试HTTP基本认证

为了测试HTTP基本认证,你可以使用工具如Postman或curl。以下是使用curl的示例:

curl -u employee:password -X GET http://localhost:8080/api/salaries

这个命令使用用户名“employee”和密码“password”来访问薪资信息API。

说明
  • 配置类:配置了HTTP基本认证和权限检查。
  • 控制器:提供了一个端点来获取薪资信息,该端点使用了@PreAuthorize注解来确保只有具有SALARY_READ权限的用户可以访问。
  • 测试:使用curl命令行工具进行基本认证测试。

5、OAuth2 和 OpenID Connect

业务场景: 一个社交网络平台允许用户通过他们的Google账号进行登录。

业务时序图

在这里插入图片描述

  1. 用户(User) 尝试访问应用程序(Application)。
  2. 应用程序(Application) 重定向用户到身份提供者(Identity Provider)以启动认证。
  3. 身份提供者(Identity Provider) 接收到认证请求,并提示用户输入凭据。
  4. 用户(User) 提供凭据进行认证。
  5. 身份提供者(Identity Provider) 验证用户凭据,并显示同意屏幕,请求用户授权应用程序访问其数据。
  6. 用户(User) 同意授权。
  7. 身份提供者(Identity Provider) 将授权码返回给应用程序。
  8. 应用程序(Application) 将授权码发送到授权服务器(Authorization Server)以换取访问令牌。
  9. 授权服务器(Authorization Server) 验证授权码并返回访问令牌给应用程序。
  10. 应用程序(Application) 使用访问令牌请求资源服务器(Resource Server)以获取资源。
  11. 资源服务器(Resource Server) 验证访问令牌并返回请求的资源数据给应用程序。
  12. 应用程序(Application) 将资源数据展示给用户。
Spring Security 配置

首先,我们需要配置Spring Security以启用OAuth2客户端支持。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistration.Builder;

@Configuration
@EnableWebSecurity
public class OAuth2Config extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .anyRequest().authenticated()
            .and()
            .oauth2Login();
    }

    @Bean
    public ClientRegistrationRepository clientRegistrationRepository() {
        ClientRegistration googleRegistration = Builder
             aClientRegistration()
             .withRegistrationId("google")
             .withClientName("Google")
             .withClientSecret("{client-secret}")
             .withAuthorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
             .withRedirectUriTemplate("{baseUrl}/oauth2/callback/{registrationId}")
             .withScope("openid", "profile", "email")
             .withClientAuthenticationMethod(ClientAuthenticationMethod.POST)
             .withAuthorizationUri("https://accounts.google.com/o/oauth2/v2/auth")
             .withTokenUri("https://oauth2.googleapis.com/token")
             .withUserInfoUri("https://openidconnect.googleapis.com/v1/userinfo")
             .build();

        return new InMemoryClientRegistrationRepository(googleRegistration);
    }
}
业务逻辑代码

创建一个控制器来处理用户资料的请求。

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @GetMapping("/profile")
    public String getUserProfile(@AuthenticationPrincipal OidcUser principal) {
        return "Hello, " + principal.getFullName() + "!";
    }
}
说明
  • 配置类:配置了Spring Security以使用OAuth2客户端。
  • 控制器:提供了一个端点来获取通过OAuth2认证的用户资料。

6、CSRF 保护(CSRF Protection)

业务场景: 一个在线购物网站,需要确保所有的交易请求都是由用户主动发起的,以防止跨站请求伪造攻击。

业务时序图

在这里插入图片描述

  1. 用户(User) 请求页面。
  2. 浏览器(Browser) 向服务器(Server)发送 HTTP GET 请求。
  3. 服务器(Server) 接收请求并由应用程序(Application)生成一个 CSRF 令牌。
  4. 应用程序(Application) 将生成的 CSRF 令牌发送回服务器。
  5. 服务器(Server) 将带有 CSRF 令牌的页面发送回浏览器。
  6. 浏览器(Browser) 接收带有 CSRF 令牌的页面并展示给用户。
  7. 用户(User) 提交表单。
  8. 浏览器(Browser) 将带有 CSRF 令牌的 HTTP POST 请求发送到服务器。
  9. CSRF 令牌生成器(CSRF Token Generator) 验证接收到的 CSRF 令牌。
  10. 应用程序(Application) 确认 CSRF 令牌有效。
  11. 服务器(Server) 对 POST 请求做出响应。
Spring Security 配置

首先,我们需要配置Spring Security以启用CSRF保护。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRepository;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/home", true)
            .and()
            .logout()
                .permitAll()
            .and()
            .csrf()  // 启用CSRF保护
                .csrfTokenRepository(csrfTokenRepository());  // 自定义CSRF令牌存储
    }

    @Bean
    public CsrfTokenRepository csrfTokenRepository() {
        // 使用Cookie存储CSRF令牌
        return CookieCsrfTokenRepository.withHttpOnlyFalse();
    }
}
业务逻辑代码

创建一个控制器来处理购物车和订单的请求。

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class ShoppingCartController {

    @GetMapping("/cart")
    public String showCart() {
        return "cart";  // 显示购物车页面
    }

    @PostMapping("/place-order")
    public String placeOrder() {
        // 处理订单逻辑
        return "order-confirmation";  // 显示订单确认页面
    }
}
说明
  • 配置类:配置了Spring Security以使用CSRF保护,使用CookieCsrfTokenRepository来存储CSRF令牌。
  • 控制器:提供了两个端点,一个用于显示购物车,另一个用于处理订单。这两个操作都需要用户已经通过认证。

7、密码编码(Password Encoding)

业务场景: 用户注册和密码重置功能,需要确保存储在数据库中的密码是加密的,以提高安全性。

业务时序图

在这里插入图片描述

  1. 用户(User) 在浏览器(Browser)中输入密码。
  2. 浏览器(Browser) 将包含密码的注册表单提交到服务器(Server)。
  3. 服务器(Server) 请求密码编码器(PasswordEncoder)对密码进行编码。
  4. 密码编码器(PasswordEncoder) 对用户输入的密码进行加密处理。
  5. 密码编码器(PasswordEncoder) 将加密后的密码返回给服务器。
  6. 服务器(Server) 将加密后的密码存储到数据库(Database)。
  7. 数据库(Database) 确认密码已存储,并通知服务器。
  8. 服务器(Server) 向浏览器发送注册成功的响应。
Spring Security 配置

首先,我们需要配置Spring Security以使用密码编码器。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/register", "/reset-password").permitAll()
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/home", true)
            .and()
            .logout()
                .permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
            .passwordEncoder(passwordEncoder())
            .withUser("user").password(passwordEncoder().encode("password")).roles("USER");
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
业务逻辑代码

创建一个服务来处理用户注册和密码重置。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * 注册新用户
     * 
     * @param username 用户名
     * @param rawPassword 明文密码
     */
    public void registerUser(String username, String rawPassword) {
        String encodedPassword = passwordEncoder.encode(rawPassword);
        // 存储用户信息和加密后的密码到数据库
        System.out.println("User registered with encoded password: " + encodedPassword);
    }

    /**
     * 重置用户密码
     * 
     * @param username 用户名
     * @param newPassword 新的明文密码
     */
    public void resetPassword(String username, String newPassword) {
        String encodedPassword = passwordEncoder.encode(newPassword);
        // 更新数据库中的密码
        System.out.println("User password reset with encoded password: " + encodedPassword);
    }
}
控制器

创建一个控制器来处理注册和密码重置的请求。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/register")
    public String register(@RequestParam String username, @RequestParam String password) {
        userService.registerUser(username, password);
        return "User registered successfully!";
    }

    @PostMapping("/reset-password")
    public String resetPassword(@RequestParam String username, @RequestParam String newPassword) {
        userService.resetPassword(username, newPassword);
        return "Password reset successfully!";
    }
}
说明
  • 配置类:配置了Spring Security,并定义了一个密码编码器 Bean,使用BCrypt算法。
  • 业务逻辑:提供了注册用户和重置密码的方法,这些方法使用密码编码器来加密密码。
  • 控制器:提供了处理注册和密码重置请求的端点。

8、方法级安全性(Method Security)

业务场景: 一个在线内容管理系统,其中某些管理功能(如删除内容)仅对具有特定权限的用户开放。

业务时序图

在这里插入图片描述

  1. 用户(User) 在浏览器(Browser)中请求一个敏感操作。
  2. 浏览器(Browser) 向服务器(Server)发送 HTTP 请求。
  3. 服务器(Server)认证管理器(AuthenticationManager) 对请求进行认证。
  4. 认证管理器(AuthenticationManager) 返回认证令牌给服务器。
  5. 服务器(Server)方法安全拦截器(MethodSecurityInterceptor) 检查安全上下文。
  6. 方法安全拦截器(MethodSecurityInterceptor) 请求 自定义方法安全服务(CustomMethodSecurityService) 评估安全表达式。
  7. 自定义方法安全服务(CustomMethodSecurityService) 返回访问决策给方法安全拦截器。
  8. 方法安全拦截器(MethodSecurityInterceptor) 根据决策允许或拒绝访问。
  9. 服务器(Server) 将响应返回给浏览器。
Spring Security 配置

首先,我们需要配置Spring Security以启用方法级安全性。

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
    // 可以在这里配置方法安全性相关的Bean,如自定义的权限评估器
}
业务逻辑代码

创建一个服务来处理内容管理,包括一个需要特定权限的方法。

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

@Service
public class ContentManagementService {

    /**
     * 删除内容
     * 
     * @param contentId 内容ID
     * @return 操作结果
     */
    @PreAuthorize("hasAuthority('CONTENT_DELETE')")
    public String deleteContent(Long contentId) {
        // 执行删除内容的逻辑
        System.out.println("Content with ID " + contentId + " has been deleted.");
        return "Deleted successfully";
    }
}
控制器

创建一个控制器来处理删除内容的请求。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ContentController {

    @Autowired
    private ContentManagementService contentManagementService;

    @DeleteMapping("/content/{contentId}")
    public String deleteContent(@PathVariable Long contentId) {
        return contentManagementService.deleteContent(contentId);
    }
}
说明
  • 配置类:启用了方法级安全性,允许使用 @PreAuthorize 和其他方法安全性注解。
  • 业务逻辑ContentManagementService 提供了删除内容的方法,该方法通过 @PreAuthorize 注解限制只有具有 CONTENT_DELETE 权限的用户可以执行。
  • 控制器ContentController 提供了一个HTTP DELETE端点来删除指定ID的内容。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Solomon_肖哥弹架构

你的欣赏就是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值