第5章 Spring Security 的高级认证技术(2024 最新版)(下)

简介: 第5章 Spring Security 的高级认证技术(2024 最新版)

第5章 Spring Security 的高级认证技术(2024 最新版)(上)+https://developer.aliyun.com/article/1487149


5.2.3 拓展案例 1:刷新令牌

在实际应用中,为了提高安全性和改善用户体验,通常采用较短的 JWT 有效期,并提供刷新令牌机制来允许用户在访问令牌过期后,无需重新登录即可获取新的访问令牌。刷新令牌通常具有比访问令牌更长的有效期,并且可以用来多次获取新的访问令牌,直到刷新令牌本身过期。

案例 Demo

假设我们正在开发一个博客系统,用户登录后将获得一个访问令牌和一个刷新令牌。

步骤 1: 扩展 JwtUtil 类

首先,我们需要扩展之前创建的 JwtUtil 类,添加生成刷新令牌的方法。

public class JwtUtil {
    private String secretKey = "secret"; // 实际应用中应更安全
    private long validityInMilliseconds = 3600000; // 访问令牌有效期: 1小时
    private long refreshTokenValidityInMilliseconds = 28_800_000; // 刷新令牌有效期: 8小时
    // 原有的 createToken 方法...
    public String createRefreshToken(String username) {
        Date now = new Date();
        Date validity = new Date(now.getTime() + refreshTokenValidityInMilliseconds);
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(now)
                .setExpiration(validity)
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
    }
    // 原有的 validateToken 和 getUsernameFromJWT 方法...
}

步骤 2: 实现刷新令牌的存储

为了跟踪刷新令牌,我们需要在服务器端存储它们。在简单的示例中,可以将刷新令牌存储在内存或数据库中。

@Component
public class RefreshTokenStore {
    private final Map<String, String> refreshTokenStore = new ConcurrentHashMap<>();
    public void storeToken(String username, String refreshToken) {
        refreshTokenStore.put(username, refreshToken);
    }
    public String getRefreshToken(String username) {
        return refreshTokenStore.get(username);
    }
    // 添加验证刷新令牌的方法等...
}

步骤 3: 处理刷新令牌请求

接下来,实现一个端点来处理刷新令牌请求,验证刷新令牌,并发放新的访问令牌。

@RestController
public class RefreshTokenController {
    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private RefreshTokenStore refreshTokenStore;
    @PostMapping("/refresh")
    public ResponseEntity<?> refreshAuthentication(@RequestParam String refreshToken) {
        // 简化示例,实际应更严格地验证刷新令牌
        String username = jwtUtil.getUsername(refreshToken);
        String storedRefreshToken = refreshTokenStore.getRefreshToken(username);
        if (refreshToken.equals(storedRefreshToken) && jwtUtil.validateToken(refreshToken)) {
            String newToken = jwtUtil.createToken(username);
            return ResponseEntity.ok(newToken);
        } else {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Invalid refresh token");
        }
    }
}

步骤 4: 调整登录逻辑

最后,调整登录逻辑以同时发放访问令牌和刷新令牌。

@PostMapping("/login")
public ResponseEntity<?> login(@RequestParam String username, @RequestParam String password) {
    // 验证用户凭据(省略)
    String token = jwtUtil.createToken(username);
    String refreshToken = jwtUtil.createRefreshToken(username);
    refreshTokenStore.storeToken(username, refreshToken);
    return ResponseEntity.ok(Map.of("accessToken", token, "refreshToken", refreshToken));
}

通过实现刷新令牌机制,你的应用可以提供更安全且用户友好的认证体验,使用户在访问令牌过期后能够轻松地继续访问应用资源,而无需频

繁地重新登录。

5.2.4 拓展案例 2:微服务间的 JWT 验证

在基于微服务架构的系统中,服务间的安全通信至关重要。利用 JWT 进行服务间的认证是一种有效的方法,可以确保只有授权的服务能够访问受保护的资源。以下是如何在 Java 中实现微服务间的 JWT 验证的案例。

案例 Demo

假设我们有两个微服务:ServiceA 和 ServiceB。ServiceA 需要调用 ServiceB 的受保护接口。

步骤 1: 在 ServiceB 中验证 JWT

首先,ServiceB 需要验证从 ServiceA 发来的请求中的 JWT。这可以通过配置一个 JWT 过滤器来完成,类似于前面提到的用户认证过程。

在 ServiceB 的 SecurityConfig 中配置 JWT 过滤器:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private JwtTokenFilter jwtTokenFilter;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
                .authorizeRequests()
                .antMatchers("/api/protected").authenticated()
                .anyRequest().permitAll()
            .and()
                .addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

步骤 2: 在 ServiceA 中生成 JWT

ServiceA 需要生成一个有效的 JWT,用于调用 ServiceB 的接口。这个 JWT 可以是由专门的认证服务生成,或者 ServiceA 本身生成,具体取决于你的架构设计。

这里假设 ServiceA 自行生成 JWT:

@Service
public class JwtService {
    public String generateTokenForServiceB() {
        // 生成 JWT 逻辑,可能包括设置特定的 claims,如服务标识
        return jwtUtil.createToken("ServiceA");
    }
}

步骤 3: ServiceA 调用 ServiceB 时携带 JWT

当 ServiceA 需要调用 ServiceB 的受保护接口时,它应在 HTTP 请求的 Authorization 头中携带 JWT。

@Service
public class ServiceAClient {
    private RestTemplate restTemplate = new RestTemplate();
    @Autowired
    private JwtService jwtService;
    public String callProtectedServiceB() {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Authorization", "Bearer " + jwtService.generateTokenForServiceB());
        HttpEntity<String> entity = new HttpEntity<>(headers);
        
        return restTemplate.exchange(
                "http://ServiceB/api/protected", HttpMethod.GET, entity, String.class).getBody();
    }
}

这个简化的例子演示了如何在微服务间使用 JWT 进行安全通信。ServiceA 生成一个 JWT 并在调用 ServiceB 时将其包含在请求中,而 ServiceB 则验证这个 JWT 来确保只有授权的服务可以访问其受保护的接口。

通过实现这种机制,你可以确保你的微服务架构中的服务间通信是安全的,只有拥有正确 JWT 的服务才能访问特定的资源或执行特定的操作。这对于构建可扩展且安全的微服务系统至关重要。

5.3 社交登录集成

社交登录集成允许用户使用他们现有的社交媒体账户,如 Facebook、Google 或 Twitter,来直接登录到其他应用或服务中,无需创建新的用户名和密码。这不仅提高了用户体验,还有助于提高注册转化率和用户参与度。

5.3.1 基础知识详解

社交登录集成,允许用户使用他们在社交平台上的账户(如 Facebook、Google 或 Twitter)来登录其他在线服务和应用,是现代网络应用中提升用户体验的重要特性。这种认证方式不仅简化了登录过程,还有助于降低新用户注册的门槛。下面是实现社交登录的一些关键基础知识点。

关键组件

  1. OAuth 2.0:
  • 社交登录的核心是基于 OAuth 2.0 授权框架,它是一个开放标准,用于提供安全的客户端应用访问资源的授权。
  1. OpenID Connect:
  • 在 OAuth 2.0 的基础上,OpenID Connect 为用户身份提供了一个标准化框架。它在 OAuth 2.0 的授权流程中添加了身份层,使得应用能够获取用户的身份信息。

OAuth 2.0 和 OpenID Connect 工作流程

  1. 用户选择社交登录:
  • 用户在应用登录页面选择使用社交平台账户登录。
  1. 重定向到社交平台:
  • 应用将用户重定向到社交平台的授权页面,请求用户授权应用访问其在该平台上的个人信息。
  1. 用户授权:
  • 用户在社交平台上登录(如果尚未登录)并授权应用访问其信息。
  1. 授权码交换:
  • 社交平台将用户重定向回应用,并附带一个授权码。应用使用此授权码向社交平台请求访问令牌。
  1. 获取用户信息:
  • 应用使用访问令牌调用社交平台的 API,获取用户信息,如姓名和邮箱地址。
实现社交登录的挑战
  • 配置多个社交登录提供者:
  • 应用可能需要支持多个社交登录平台,每个平台都有其特定的配置要求和流程。
  • 安全性:
  • 需要确保在整个授权过程中保护用户信息和访问令牌的安全,防止中间人攻击等安全威胁。
  • 用户数据一致性:
  • 应用需要处理将社交平台返回的用户信息与应用内现有用户记录正确匹配和同步的逻辑。
  • 用户体验:
  • 社交登录应无缝集成到应用的登录和注册流程中,提供简洁明了的用户界面和指引。

通过深入理解社交登录的基础知识和相关挑战,开发者可以更有效地集成和管理社交登录功能,为用户提供安全、便捷的登录体验,同时确保应用的安全性和用户数据的一致性。

5.3.2 重点案例:集成 Google 社交登录

社交登录极大地简化了用户的登录过程,并提高了用户体验。在本案例中,我们将通过实际示例演示如何在 Spring Boot 应用中集成 Google 社交登录。

案例 Demo

步骤 1: 添加依赖

为了支持社交登录,首先需要在 pom.xml 中添加 Spring Security OAuth2 客户端依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

步骤 2: 配置 OAuth2 客户端

在 application.yml 文件中配置 Google 作为 OAuth2 客户端。你需要前往 Google Cloud Platform 创建一个 OAuth 2.0 客户端 ID 并获取客户端 ID 和客户端密钥。

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            clientId: <YOUR_CLIENT_ID>
            clientSecret: <YOUR_CLIENT_SECRET>
            scope:
              - email
              - profile

步骤 3: 创建登录控制器

实现一个控制器来处理登录成功后的逻辑。Spring Security 会自动重定向到 /login/oauth2/code/google 路径以处理 OAuth2 登录流程,但我们可以定义一个控制器来处理登录成功后的重定向。

@Controller
public class SocialLoginController {
    @GetMapping("/loginSuccess")
    public String getLoginInfo(@AuthenticationPrincipal OAuth2User principal, Model model) {
        String name = principal.getAttribute("name");
        model.addAttribute("name", name);
        return "loginSuccess";
    }
}

在这个控制器中,@AuthenticationPrincipal OAuth2User principal 参数用于接收 OAuth2 登录成功后的用户信息。

步骤 4: 配置安全性

确保 Spring Security 配置支持 OAuth2 登录,并正确处理登录成功后的重定向。

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests(authorizeRequests ->
                authorizeRequests
                    .antMatchers("/", "/error", "/webjars/**").permitAll()
                    .anyRequest().authenticated()
            )
            .oauth2Login(oauth2Login ->
                oauth2Login
                    .loginPage("/login")
                    .defaultSuccessUrl("/loginSuccess", true)
            );
    }
}

在这个配置中,.defaultSuccessUrl("/loginSuccess", true) 指定了登录成功后的重定向 URL。

测试社交登录

启动应用并访问主页,你应该会看到一个选项让用户通过 Google 账户登录。登录成功后,用户将被重定向到 /loginSuccess 路径,并显示用户的名称。

通过这个案例,你可以看到集成 Google 社交登录的基本步骤。这种方法不仅为用户提供了便捷的登录方式,还可以通过用户的社交账户信息来丰富应用的用户体验。

5.3.3 拓展案例 1:集成 Facebook 登录

在现代Web应用中,提供多种社交登录选项可以显著提升用户体验。在本案例中,我们将探讨如何在Spring Boot应用中集成Facebook社交登录,以便用户可以使用他们的Facebook账户进行登录。

案例 Demo

步骤 1: 添加依赖

确保你的pom.xml中已经包含了Spring Security OAuth2客户端依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

步骤 2: 配置 OAuth2 客户端

在application.yml中配置Facebook作为OAuth2客户端。首先,你需要在Facebook开发者平台创建一个应用,获取客户端ID和客户端密钥。

spring:
  security:
    oauth2:
      client:
        registration:
          facebook:
            clientId: <YOUR_FACEBOOK_CLIENT_ID>
            clientSecret: <YOUR_FACEBOOK_CLIENT_SECRET>
            scope:
              - email
              - public_profile

步骤 3: 创建登录成功控制器

创建一个控制器来处理登录成功后的逻辑。Spring Security将自动处理OAuth2登录流程,我们只需要定义登录成功后的行为。

@Controller
public class SocialLoginController {
    @GetMapping("/loginSuccess")
    public String loginSuccess(@AuthenticationPrincipal OAuth2User principal, Model model) {
        String name = principal.getAttribute("name");
        model.addAttribute("name", name);
        // 可以添加更多的用户信息到模型
        return "loginSuccess";
    }
}

步骤 4: 配置 Spring Security

更新你的Spring Security配置以支持OAuth2登录,并处理登录成功后的重定向。

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests(authorizeRequests ->
                authorizeRequests
                    .antMatchers("/", "/error", "/webjars/**").permitAll()
                    .anyRequest().authenticated()
            )
            .oauth2Login(oauth2Login ->
                oauth2Login
                    .loginPage("/login")
                    .defaultSuccessUrl("/loginSuccess", true)
            );
    }
}

在这个配置中,.defaultSuccessUrl("/loginSuccess", true)指定了登录成功后的重定向URL。

测试 Facebook 登录

启动应用并访问主页,你现在应该可以看到一个选项让用户通过Facebook账户登录。登录成功后,用户将被重定向到/loginSuccess路径,并可以看到他们的名称等信息。

通过集成Facebook登录,你的应用不仅为用户提供了一种便利的登录方式,还可以利用Facebook提供的丰富用户数据来提升应用的个性化体验。这种社交登录集成方式能够有效提高用户的注册和登录率,从而提升整体的用户参与度。

5.3.4 拓展案例 2:自定义用户注册和登录流程

在集成社交登录(如Google或Facebook)后,应用通常需要将社交账户与现有的用户账户系统集成。这可能涉及到在用户首次使用社交账户登录时创建一个新的应用内用户记录,或者将社交账户链接到现有的用户记录。以下是如何在Spring Boot应用中自定义用户注册和登录流程的示例。

案例 Demo

步骤 1: 定义用户实体

首先,定义一个用户实体来存储用户信息。在这个实体中,你可以存储用户的基本信息以及与社交账户相关的标识符(如Google的sub字段)。

@Entity
public class ApplicationUser {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username; // 可能是电子邮件地址
    private String name;
    private String googleSub; // 用于存储Google账户的唯一标识符
    // 构造函数、Getter和Setter省略
}

步骤 2: 实现用户服务

实现一个用户服务来处理用户的创建和查找逻辑。这个服务将检查使用社交账户登录的用户是否已经存在于系统中,并据此创建新用户或更新现有用户记录。

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    public ApplicationUser processUserLogin(OAuth2User oAuth2User) {
        String googleSub = oAuth2User.getAttribute("sub"); // 从Google登录信息中获取sub
        ApplicationUser user = userRepository.findByGoogleSub(googleSub);
        if (user == null) {
            user = new ApplicationUser();
            user.setGoogleSub(googleSub);
        }
        user.setUsername(oAuth2User.getAttribute("email"));
        user.setName(oAuth2User.getAttribute("name"));
        return userRepository.save(user);
    }
}

步骤 3: 自定义OAuth2登录成功处理器

创建一个登录成功处理器,它在用户通过社交账户登录成功后,调用UserService来处理用户记录。

@Component
public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
    @Autowired
    private UserService userService;
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws ServletException, IOException {
        OAuth2AuthenticationToken authToken = (OAuth2AuthenticationToken) authentication;
        OAuth2User oAuth2User = authToken.getPrincipal();
        ApplicationUser user = userService.processUserLogin(oAuth2User);
        
        // 可以在这里将用户信息添加到会话中
        super.onAuthenticationSuccess(request, response, authentication);
    }
}

步骤 4: 配置Spring Security使用自定义成功处理器

在Spring Security配置中,指定OAuth2登录成功后使用自定义的成功处理器。

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private CustomAuthenticationSuccessHandler successHandler;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // 其他配置...
            .oauth2Login(oauth2Login ->
                oauth2Login
                    .successHandler(successHandler)
            );
    }
}

测试自定义流程

现在,当用户通过Google社交账户登录时,应用将自动检查用户是否已存在,并据此创建或更新用户记录。登录成功后,用户将被重定向到默认成功URL,此时用户的会话已经包含了其信息。

通过这个案例,你可以看到如何将社交登录与自定义的用户注册和登录流程集成。这种方法不仅能够提高用户体验,还能够确保应用能够有效地管理和利用用户数据。

目录
相关文章
|
SQL Java 数据库连接
Spring Data JPA 技术深度解析与应用指南
本文档全面介绍 Spring Data JPA 的核心概念、技术原理和实际应用。作为 Spring 生态系统中数据访问层的关键组件,Spring Data JPA 极大简化了 Java 持久层开发。本文将深入探讨其架构设计、核心接口、查询派生机制、事务管理以及与 Spring 框架的集成方式,并通过实际示例展示如何高效地使用这一技术。本文档约1500字,适合有一定 Spring 和 JPA 基础的开发者阅读。
1017 0
|
前端开发 Java API
利用 Spring WebFlux 技术打造高效非阻塞 API 的完整开发方案与实践技巧
本文介绍了如何使用Spring WebFlux构建高效、可扩展的非阻塞API,涵盖响应式编程核心概念、技术方案设计及具体实现示例,适用于高并发场景下的API开发。
841 0
|
安全 Java Apache
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 身份和权限认证
本文介绍了 Apache Shiro 的身份认证与权限认证机制。在身份认证部分,分析了 Shiro 的认证流程,包括应用程序调用 `Subject.login(token)` 方法、SecurityManager 接管认证以及通过 Realm 进行具体的安全验证。权限认证部分阐述了权限(permission)、角色(role)和用户(user)三者的关系,其中用户可拥有多个角色,角色则对应不同的权限组合,例如普通用户仅能查看或添加信息,而管理员可执行所有操作。
745 0
|
监控 安全 Java
Spring Cloud 微服务治理技术详解与实践指南
本文档全面介绍 Spring Cloud 微服务治理框架的核心组件、架构设计和实践应用。作为 Spring 生态系统中构建分布式系统的标准工具箱,Spring Cloud 提供了一套完整的微服务解决方案,涵盖服务发现、配置管理、负载均衡、熔断器等关键功能。本文将深入探讨其核心组件的工作原理、集成方式以及在实际项目中的最佳实践,帮助开发者构建高可用、可扩展的分布式系统。
706 1
|
监控 Kubernetes Cloud Native
Spring Batch 批处理框架技术详解与实践指南
本文档全面介绍 Spring Batch 批处理框架的核心架构、关键组件和实际应用场景。作为 Spring 生态系统中专门处理大规模数据批处理的框架,Spring Batch 为企业级批处理作业提供了可靠的解决方案。本文将深入探讨其作业流程、组件模型、错误处理机制、性能优化策略以及与现代云原生环境的集成方式,帮助开发者构建高效、稳定的批处理系统。
991 1
|
Java 数据库连接 开发者
Spring Framework 核心技术详解
本文档旨在深入解析 Java Spring Framework 的核心技术原理与应用。与侧重于快速开发的 Spring Boot 不同,本文将聚焦于 Spring 框架本身的设计理念、核心容器、控制反转(IoC)、面向切面编程(AOP)、数据访问与事务管理等基础且强大的模块。通过理解这些核心概念,开发者能够更深刻地领悟 Spring 生态系统的设计哲学,并具备解决复杂企业级应用开发问题的能力。
693 4
|
Java 应用服务中间件 开发者
Spring Boot 技术详解与应用实践
本文档旨在全面介绍 Spring Boot 这一广泛应用于现代企业级应用开发的框架。内容将涵盖 Spring Boot 的核心概念、核心特性、项目自动生成与结构解析、基础功能实现(如 RESTful API、数据访问)、配置管理以及最终的构建与部署。通过本文档,读者将能够理解 Spring Boot 如何简化 Spring 应用的初始搭建和开发过程,并掌握其基本使用方法。
871 2
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
851 70
|
人工智能 Java 程序员
Java程序员在AI时代必会的技术:Spring AI
在AI时代,Java程序员需掌握Spring AI技术以提升竞争力。Spring AI是Spring框架在AI领域的延伸,支持自然语言处理、机器学习集成与自动化决策等场景。它简化开发流程,无缝集成Spring生态,并提供对多种AI服务(如OpenAI、阿里云通义千问)的支持。本文介绍Spring AI核心概念、应用场景及开发步骤,含代码示例,助你快速入门并构建智能化应用,把握AI时代的机遇。
2952 61
|
监控 Java API
Spring WebFlux 响应式编程技术详解与实践指南
本文档全面介绍 Spring WebFlux 响应式编程框架的核心概念、架构设计和实际应用。作为 Spring 5 引入的革命性特性,WebFlux 提供了完全的响应式、非阻塞的 Web 开发栈,能够显著提升系统的并发处理能力和资源利用率。本文将深入探讨 Reactor 编程模型、响应式流规范、WebFlux 核心组件以及在实际项目中的最佳实践,帮助开发者构建高性能的响应式应用系统。
1579 0