第1章 Spring Security 概述(2024 最新版)(下)

简介: 第1章 Spring Security 概述(2024 最新版)

第1章 Spring Security 概述(2024 最新版)(上)+https://developer.aliyun.com/article/1487138

1.2.3 拓展案例 1:表单登录与数据库用户存储

在这个案例中,我们将演示如何结合表单登录和数据库用户存储在 Spring Boot 应用中实现用户认证。这将涉及从数据库中加载用户信息,并根据这些信息进行认证。

案例 Demo

假设我们有一个应用程序,其中用户信息存储在数据库中。我们需要实现一个表单登录页面,用户可以使用其数据库中的凭据进行登录。

实现步骤:

  1. 添加依赖:
    pom.xml 中添加 spring-boot-starter-data-jpa 和数据库相关的依赖,例如 spring-boot-starter-data-jpah2(作为示例数据库)。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>
  1. 配置数据源:
    application.properties 中配置数据库连接信息。对于 H2 数据库,可以使用内存模式。
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
  1. 创建用户模型和仓库:
    定义一个用户实体类和一个继承 JpaRepository 的接口。
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class User {
    @Id
    private String username;
    private String password;
    // 省略 getter 和 setter
}
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, String> {
}
  1. 实现 UserDetailsService:
    实现 UserDetailsService 接口,从数据库中加载用户信息。
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired
    private UserRepository userRepository;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findById(username)
            .orElseThrow(() -> new UsernameNotFoundException("User not found"));
        return new User(user.getUsername(), user.getPassword(), new ArrayList<>());
    }
}
  1. 配置 Spring Security:
    配置 WebSecurityConfigurerAdapter,使用自定义的 UserDetailsService 和合适的密码编码器。
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService)
        .passwordEncoder(new BCryptPasswordEncoder());
}
  1. 创建登录表单:
    创建一个简单的 HTML 登录表单。
  2. 运行和测试:
    运行应用程序,并通过访问 http://localhost:8080/login 来测试登录功能。

通过这个案例,您可以了解如何结合表单登录和数据库用户存储来实现认证功能。这在实际开发中非常常见,为应用程序提供了更大的灵活性和安全性。

1.2.4 拓展案例 2:整合 OAuth2

在这个案例中,我们将演示如何在 Spring Boot 应用中整合 OAuth2,实现第三方登录。我们将以 Google 作为 OAuth2 提供商的例子来实现这一功能。

案例 Demo

假设我们有一个 Spring Boot 应用,需要让用户可以通过他们的 Google 账户登录。

实现步骤:

  1. 添加 OAuth2 客户端依赖:
    pom.xml 中添加 spring-boot-starter-oauth2-client 依赖。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
  1. 配置 OAuth2 客户端:
    application.propertiesapplication.yml 中配置 Google 的 OAuth2 客户端信息。你需要替换 client-idclient-secret 为你在 Google API Console 中获取的实际值。
spring.security.oauth2.client.registration.google.client-id=your-google-client-id
spring.security.oauth2.client.registration.google.client-secret=your-google-client-secret
spring.security.oauth2.client.registration.google.scope=profile,email
  1. 安全配置:
    修改你的 SecurityConfig 类,以支持 OAuth2 登录。
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .anyRequest().authenticated()
            .and()
        .oauth2Login();
}
  1. 这里我们配置了所有请求都需要认证,并且启用了 OAuth2 登录。
  2. 获取用户信息:
    创建一个控制器来处理登录后的用户信息。
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class UserController {
    @GetMapping("/user")
    public String user(OAuth2AuthenticationToken token) {
        // 这里可以获取到用户信息
        // token.getPrincipal().getAttributes();
        return "user";
    }
}
  1. 运行和测试:
    启动应用程序,并尝试通过访问 http://localhost:8080/user 使用 Google 账户登录。检查用户信息是否正确获取。

通过这个案例,您可以看到在 Spring Boot 应用中整合 OAuth2 并不复杂。这使得应用可以利用第三方服务提供商的强大认证机制,同时提高了用户体验。

1.3 Spring Security 的发展历程

1.3.1 基础知识详解

Spring Security 的演变

  1. 从 Acegi 到 Spring Security:
  • Acegi Security: 最初作为一个独立的安全框架,Acegi Security 为 Spring 应用提供了强大的安全性支持。
  • Spring Security 2.0: Acegi Security 被正式纳入 Spring 框架,并更名为 Spring Security。这个版本主要专注于简化配置,并引入了新的命名空间和注解驱动的安全配置。
  1. 功能增强和改进:
  • Spring Security 3.0: 引入了多项新特性,包括对注解的支持,使得开发者可以更灵活地配置安全策略。
  • Spring Security 4.0: 进一步增强了对 Java 配置的支持,减少了对 XML 的依赖。同时,增强了对 REST API 的安全支持。
  1. 适应现代应用的需求:
  • Spring Security 5.0: 标志着对现代应用安全的重大更新,包括对 OAuth2 和 OpenID Connect 的支持,以及对 Reactive 应用的安全支持。

核心特性的演进

  • 认证和授权: 从基本的表单登录和 HTTP 基本认证到支持 LDAP、OAuth2、OpenID Connect 和 JWT 等多样化认证方式。
  • 安全性控制的细化: 引入方法级别的安全控制,允许在不同方法上应用不同的安全策略。
  • 对 RESTful API 的支持: 随着 RESTful API 在现代应用中的普及,Spring Security 加强了对无状态 REST API 的安全性支持,例如通过 JWT 实现无状态认证。
  • 易用性和灵活性: 通过提供更多的 Java 配置选项,减少了对 XML 的依赖,使得配置更加灵活和易于理解。
  • 响应式编程的支持: 为适应 Spring WebFlux 和响应式编程模型的兴起,Spring Security 5 引入了对响应式应用的支持。

通过理解 Spring Security 的发展历程和其核心特性的演进,开发者可以更好地把握其使用方式和最佳实践,从而为应用程序提供强大而灵活的安全解决方案。

1.3.2 主要案例:表达式驱动的安全性

在这个案例中,我们将展示如何在 Spring Boot 应用中使用 Spring Security 的表达式驱动安全性,以实现更细粒度的访问控制。

案例 Demo

假设我们正在开发一个网上书店应用,需要实现以下安全需求:只有管理员可以添加书籍,而注册用户可以浏览书籍。

实现步骤:

  1. 创建 Spring Boot 应用程序:
    使用 Spring Initializr 创建一个新的 Spring Boot 项目,并添加 spring-boot-starter-webspring-boot-starter-security 依赖。
  2. 配置安全性:
    创建一个继承自 WebSecurityConfigurerAdapter 的配置类并开启方法级安全。
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    // 配置用户认证逻辑
}
  1. 设置用户角色和权限:
    configure(AuthenticationManagerBuilder auth) 方法中设置用户角色和权限。
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
        .withUser("admin").password("{noop}admin123").roles("ADMIN")
        .and()
        .withUser("user").password("{noop}user123").roles("USER");
}
  1. 使用安全表达式:
    在你的服务类中使用安全注解来限制方法访问。
import org.springframework.security.access.prepost.PreAuthorize;
@Service
public class BookService {
    @PreAuthorize("hasRole('ADMIN')")
    public void addBook(Book book) {
        // 逻辑添加书籍
    }
    @PreAuthorize("hasAnyRole('USER', 'ADMIN')")
    public List<Book> listBooks() {
        // 返回书籍列表
    }
}
  1. 创建控制器:
    实现一个控制器,调用 BookService 的方法。
@RestController
public class BookController {
    @Autowired
    private BookService bookService;
    @PostMapping("/books")
    public ResponseEntity<String> addBook(@RequestBody Book book) {
        bookService.addBook(book);
        return ResponseEntity.ok("Book added successfully");
    }
    @GetMapping("/books")
    public ResponseEntity<List<Book>> getBooks() {
        return ResponseEntity.ok(bookService.listBooks());
    }
}
  1. 测试安全性:
    运行应用程序,并使用不同角色的用户尝试访问 /books 的 GET 和 POST 方法。

通过这个案例,您可以看到如何在实际项目中使用 Spring Security 的表达式驱动安全性来实现基于角色的细粒度访问控制。这种方法为应用程序提供了强大的安全性和灵活性。

1.3.3 拓展案例 1:OAuth2 集成

在这个案例中,我们将展示如何在 Spring Boot 应用中整合 OAuth2,以便用户可以使用外部服务提供商进行认证。我们以集成 GitHub 作为 OAuth2 提供商为例。

案例 Demo

假设我们有一个 Spring Boot 应用,我们希望用户能够使用他们的 GitHub 账户登录。

实现步骤:

  1. 添加 OAuth2 客户端依赖:
    pom.xml 中添加 spring-boot-starter-oauth2-client 依赖。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
  1. 配置 OAuth2 客户端:
    application.propertiesapplication.yml 中配置 GitHub 的 OAuth2 客户端信息。你需要在 GitHub 创建 OAuth 应用以获取 client-idclient-secret
spring.security.oauth2.client.registration.github.client-id=your-github-client-id
spring.security.oauth2.client.registration.github.client-secret=your-github-client-secret
spring.security.oauth2.client.registration.github.scope=user:email
  1. 安全配置:
    修改你的 SecurityConfig 类,以支持 OAuth2 登录。
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .anyRequest().authenticated()
            .and()
        .oauth2Login();
}
  1. 这里我们配置了所有请求都需要认证,并且启用了 OAuth2 登录。
  2. 创建用户信息端点:
    创建一个控制器来显示登录用户的信息。
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
    @GetMapping("/user")
    public Map<String, Object> user(@AuthenticationPrincipal OAuth2User principal) {
        return Collections.singletonMap("name", principal.getAttribute("name"));
    }
}
  1. 运行和测试:
    启动应用程序,并尝试访问 http://localhost:8080/user。你应该能够看到一个重定向到 GitHub 的登录页面。使用你的 GitHub 凭据登录后,应用将显示你的 GitHub 用户名。

通过这个案例,你可以了解到如何在 Spring Boot 应用中实现 OAuth2 集成,从而允许用户使用外部服务提供商的账户进行认证,这对于提升用户体验和应用安全性都非常有帮助。

1.3.4 拓展案例 2:JWT 认证

在这个案例中,我们将演示如何在 Spring Boot 应用中使用 JSON Web Tokens (JWT) 实现无状态认证。这对于保护 REST API 尤其重要。

案例 Demo

假设我们正在开发一个提供 REST API 的应用程序,我们希望通过 JWT 来认证和授权用户。

实现步骤:

  1. 添加 JWT 依赖:
    pom.xml 中添加处理 JWT 的依赖库,例如 java-jwt
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.3</version>
</dependency>
  1. 实现 JWT Token 服务:
    创建一个服务类来生成和验证 JWT。
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.Date;
public class JwtTokenService {
    private static final String SECRET = "YourSecretKey";
    public String generateToken(String username) {
        return JWT.create()
                .withSubject(username)
                .withExpiresAt(new Date(System.currentTimeMillis() + 86400000)) // 1 day
                .sign(Algorithm.HMAC256(SECRET));
    }
    public String validateTokenAndGetUsername(String token) {
        return JWT.require(Algorithm.HMAC256(SECRET))
                .build()
                .verify(token)
                .getSubject();
    }
}
  1. 创建 JWT 过滤器:
    创建一个 JWT 过滤器来拦截请求并验证 JWT。
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
public class JwtFilter extends OncePerRequestFilter {
    private JwtTokenService jwtTokenService = new JwtTokenService();
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
        String token = request.getHeader("Authorization");
        if (token != null && token.startsWith("Bearer ")) {
            token = token.substring(7);
            String username = jwtTokenService.validateTokenAndGetUsername(token);
            if (username != null) {
                UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, null, null);
                SecurityContextHolder.getContext().setAuthentication(auth);
            }
        }
        filterChain.doFilter(request, response);
    }
}
  1. 配置 Spring Security:
    WebSecurityConfigurerAdapter 中配置 JWT 过滤器。
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .addFilter(new JwtFilter())
        .authorizeRequests()
        .antMatchers("/api/protected").authenticated()
        .antMatchers("/api/public").permitAll();
}
  1. 创建测试端点:
    创建一些测试用的 RESTful 端点。
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
    @GetMapping("/api/protected")
    public String protectedHello() {
        return "Hello from protected API";
    }
    @GetMapping("/api/public")
    public String publicHello() {
        return "Hello from public API";
    }
}
  1. 运行和测试:
    启动应用程序,并使用生成的 JWT 尝试访问 /api/protected。没有有效 JWT 的请求应该被拒绝。

通过这个案例,您可以看到如何在 Spring Boot 应用中实现基于 JWT 的认证。JWT 认证提供了一种有效的方式来保护 REST API,确保只有拥有有效令牌的用户才能访问受保护的资源。

目录
相关文章
|
8天前
|
安全 Java 数据安全/隐私保护
|
12天前
|
存储 安全 Java
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)(下)
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)
20 2
|
12天前
|
安全 Cloud Native Java
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)(上)
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)
24 2
|
12天前
|
存储 安全 Java
第9章 Spring Security 的测试与维护 (2024 最新版)(下)
第9章 Spring Security 的测试与维护 (2024 最新版)
20 1
|
12天前
|
安全 Java 测试技术
第9章 Spring Security 的测试与维护 (2024 最新版)(上)
第9章 Spring Security 的测试与维护 (2024 最新版)
21 0
|
12天前
|
缓存 Java 数据库
第8章 Spring Security 的常见问题与解决方案(2024 最新版)(下)
第8章 Spring Security 的常见问题与解决方案(2024 最新版)
23 0
|
12天前
|
安全 Java 数据安全/隐私保护
第8章 Spring Security 的常见问题与解决方案(2024 最新版)(上)
第8章 Spring Security 的常见问题与解决方案(2024 最新版)
32 0
|
2月前
|
Java 应用服务中间件 Maven
SpringBoot 项目瘦身指南
SpringBoot 项目瘦身指南
46 0
|
2月前
|
缓存 安全 Java
Spring Boot 面试题及答案整理,最新面试题
Spring Boot 面试题及答案整理,最新面试题
122 0
|
1月前
|
存储 JSON Java
SpringBoot集成AOP实现每个接口请求参数和返回参数并记录每个接口请求时间
SpringBoot集成AOP实现每个接口请求参数和返回参数并记录每个接口请求时间
28 2