【Spring底层原理高级进阶】【SpringCloud整合Spring Security OAuth2】深入了解 Spring Security OAuth2:底层解析+使用方法+实战

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 【Spring底层原理高级进阶】【SpringCloud整合Spring Security OAuth2】深入了解 Spring Security OAuth2:底层解析+使用方法+实战

1 简介


1.1 什么是OAuth2协议?


OAuth2(Open Authorization 2.0)是一种用于授权的开放标准协议,用于通过第三方应用程序访问用户在某个服务提供商上存储的资源,而无需共享用户的凭证(例如用户名和密码)。它允许用户授权给第三方应用程序访问受保护的资源,同时确保用户的凭证信息不被直接暴露给第三方应用程序。


OAuth2协议的设计目标是简化授权流程和提高安全性,通过委托授权的方式和使用令牌来实现用户和第三方应用程序之间的安全通信。它已成为许多互联网服务提供商和开发者在构建应用程序时常用的授权标准。


1.2 OAuth2的作用和重要性


OAuth2的作用是实现用户授权和资源访问的标准化流程,同时提供了一种安全和可扩展的方式来管理第三方应用程序访问用户资源的权限。以下是OAuth2的一些重要作用:


  • 用户授权:OAuth2允许用户自主选择授权给第三方应用程序访问特定资源的权限,从而保护用户的隐私和数据安全。
  • 无需共享凭证:OAuth2通过令牌的方式实现授权,使得用户的凭证信息(如用户名和密码)不需要被共享给第三方应用程序,提高了安全性。
  • 安全通信:OAuth2使用令牌来代表用户的身份和权限,确保用户和第三方应用程序之间的通信是安全和可信的。
  • 权限管理:OAuth2提供了对用户资源访问权限的细粒度控制,使得用户可以选择性地授权不同的权限给不同的应用程序。


1.3 Spring Security OAuth2简介


Spring Security OAuth2是Spring Security框架的一个扩展模块,用于实现基于OAuth2协议的身份验证和授权功能。它提供了一套易于使用和集成的API,方便开发者在Spring应用程序中实现OAuth2的各种授权模式和流程。


Spring Security OAuth2扩展了Spring Security的功能,提供了配置和管理OAuth2的客户端、授权服务器、令牌存储、权限管理等功能。它使得开发者可以轻松地构建安全的OAuth2服务和客户端应用程序。


现在,让我们深入了解OAuth2协议的流程和不同的授权模式。


2.OAuth2协议的流程


2.1 客户端注册和授权服务器配置


在OAuth2中,首先需要进行客户端的注册和配置授权服务器。客户端是指需要访问受保护资源的应用程序,授权服务器负责验证用户身份并颁发访问令牌。


以下是一个示例代码片段,演示如何在Spring Security中进行客户端注册和授权服务器的配置:

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
 
    @Autowired
    private AuthenticationManager authenticationManager;
 
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient("client1")
            .secret(passwordEncoder().encode("client1secret"))
            .authorizedGrantTypes("authorization_code", "refresh_token")
            .scopes("read", "write")
            .redirectUris("http://localhost:8080/callback");
    }
 
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager);
    }
 
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

在上述代码中,我们使用了@EnableAuthorizationServer注解来启用授权服务器,并通过configure(ClientDetailsServiceConfigurer clients)方法配置了一个内存中的客户端。客户端ID为"client1",密码为"client1secret",授权模式为"authorization_code"和"refresh_token",授权范围为"read"和"write",回调URL为"http://localhost:8080/callback"。


2.2 授权码模式(Authorization Code Grant)


授权码模式是OAuth2中最常用的一种授权模式。在这种模式下,客户端通过重定向用户到授权服务器的登录页面,用户登录并同意授权后,授权服务器将授权码返回给客户端。然后,客户端使用授权码向授权服务器请求访问令牌。


以下是授权码模式的流程示例代码:

@Controller
public class AuthorizationCodeController {
 
    @GetMapping("/authorize")
    public String authorize(@RequestParam("response_type") String responseType,
                            @RequestParam("client_id") String clientId,
                            @RequestParam("redirect_uri") String redirectUri,
                            @RequestParam("scope") String scope,
                            @RequestParam("state") String state) {
        // 处理授权请求,并返回授权页面
    }
 
    @GetMapping("/callback")
    public String callback(@RequestParam("code") String code,
                           @RequestParam("state") String state) {
        // 处理授权码回调,并向授权服务器请求访问令牌
    }
}

在上述代码中,/authorize端点用于处理授权请求,通过重定向用户到授权服务器的登录页面,用户登录并同意授权后,授权服务器将授权码返回给客户端。然后,客户端会将用户重定向到redirect_uri指定的回调URL,并在回调URL中接收授权码。


/callback端点用于处理授权码回调,客户端通过回调URL接收到授权码后,可以向授权服务器发起请求,使用授权码获取访问令牌。


类似的,还有简化模式、密码模式、客户端凭证模式和刷新令牌等授权模式。每个授权模式都有不同的流程和代码实现方式。


以下是OAuth2流程示例代码:

@Controller
public class OAuth2Controller {
 
    @Autowired
    private OAuth2RestTemplate restTemplate;
 
    @GetMapping("/authorize")
    public String authorize() {
        // 重定向用户到授权服务器登录页面
    }
 
    @GetMapping("/callback")
    public String callback(@RequestParam("code") String code) {
        // 使用授权码向授权服务器请求访问令牌
    }
 
    @GetMapping("/protected-resource")
    public String protectedResource() {
        // 使用访问令牌访问受保护的资源
    }
}

在上述代码中,/authorize端点用于重定向用户到授权服务器的登录页面。用户登录并同意授权后,授权服务器将用户重定向回客户端的回调URL,并在URL中附带授权码。


/callback端点用于处理授权码回调,客户端通过回调URL接收到授权码后,可以使用授权码向授权服务器请求访问令牌。


/protected-resource端点用于示范如何使用访问令牌访问受保护的资源。在实际应用中,你可以使用访问令牌来访问需要授权的API或资源。


3.OAuth2协议的原理


3.1 OAuth2中的角色和概念:


在OAuth2协议中,有以下几个核心角色和概念:


  • 资源所有者(Resource Owner):即用户或系统的代表,拥有受保护资源的所有权。
  • 客户端(Client):代表资源所有者与授权服务器进行交互的应用程序。可以是Web应用程序、移动应用程序或第三方服务。
  • 授权服务器(Authorization Server):负责验证资源所有者的身份并颁发访问令牌(Access Token)给客户端。它通常是一个独立的服务器,可以与资源服务器分离或合并。
  • 资源服务器(Resource Server):存储受保护的资源,并根据令牌的有效性进行访问控制。资源服务器可以是一个或多个服务,可以与授权服务器分离或合并。
  • 授权许可(Authorization Grant):资源所有者授权客户端访问受保护资源的凭证,如授权码、隐式授权、密码授权、客户端凭证等。
  • 令牌(Token):用于表示授权许可的凭证,包括访问令牌、刷新令牌和身份令牌等。
  • 令牌端点(Token Endpoint):客户端与授权服务器交互以获取或刷新令牌的API端点。


3.2 令牌(Token)的生成和验证:


在OAuth2中,令牌是用于表示授权许可的凭证。通常,令牌由授权服务器生成,并在客户端和资源服务器之间传递和验证。


令牌的生成和验证过程可以通过以下示例代码来说明:

// 生成访问令牌的示例代码
String generateAccessToken() {
    // 生成随机的访问令牌字符串
    String accessToken = generateRandomToken();
    
    // 设置访问令牌的过期时间
    Date expiration = calculateExpirationDate();
    
    // 保存访问令牌到数据库或缓存中
    saveAccessToken(accessToken, expiration);
    
    return accessToken;
}
 
// 验证访问令牌的示例代码
boolean validateAccessToken(String accessToken) {
    // 从数据库或缓存中获取访问令牌和过期时间
    AccessToken storedToken = getAccessTokenFromDatabase(accessToken);
    
    // 检查访问令牌是否存在且未过期
    if (storedToken != null && storedToken.isExpired()) {
        return false;
    }
    
    return true;
}

在上述示例中,generateAccessToken方法用于生成随机的访问令牌,并设置其过期时间,然后将访问令牌保存到数据库或缓存中。validateAccessToken方法用于验证传入的访问令牌是否有效,通过从数据库或缓存中获取令牌并检查其是否存在且未过期来进行验证。


3.3 授权服务器和资源服务器的交互:


在OAuth2协议中,授权服务器和资源服务器之间进行交互来验证令牌的有效性和授权许可。


以下是授权服务器和资源服务器进行交互的示例代码:

// 授权服务器验证访问令牌的示例代码
boolean validateAccessToken(String accessToken) {
    // 向资源服务器发送验证请求
    boolean isValid = resourceServer.validateToken(accessToken);
    
    return isValid;
}
 
// 资源服务器验证访问令牌的示例代码
boolean validateToken(String accessToken) {
    // 从数据库或缓存中获取访问令牌和过期时间
    AccessToken storedToken = getAccessTokenFromDatabase(accessToken);
    
    // 检查访问令牌是否存在且未过期
    if (storedToken != null && storedToken.isExpired()) {
        return false;
    }
    
    return true;
}


3.4 安全性考虑和防护措施:(整合SpringCloud)


在实施OAuth2协议时,需要考虑以下安全性问题和采取相应的防护措施:


  1. 令牌的安全传输:令牌在客户端和服务器之间传输时应进行安全加密,以防止令牌被拦截和篡改。可以使用HTTPS协议来保护令牌的传输安全。


在客户端与服务器建立连接时,客户端发送一个HTTPS请求。服务器会返回一个包含公钥的证书,客户端使用该公钥来加密对称密钥,并将加密后的密钥发送给服务器。服务器使用私钥解密对称密钥,并与客户端建立安全连接。


在Spring Cloud中,可以通过配置Spring Security来启用HTTPS。首先,需要生成SSL证书,并将其配置到应用程序中。然后,在Spring Security的配置类中添加以下代码:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requiresChannel()
                .anyRequest().requiresSecure();
    }
}


  1. 令牌的保密性:令牌应仅由授权服务器和资源服务器持有,并且不应通过客户端或其他不受信任的渠道传播。客户端应采取适当的安全措施,如存储令牌时进行加密处理。


在Spring Cloud中,可以使用Spring Security OAuth2来实现令牌的保密性。在授权服务器和资源服务器中,可以配置加密算法和密钥来对令牌进行加密处理。

授权服务器配置:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
 
    // 密钥存储在数据库或配置文件中
    private static final String SECRET_KEY = "your-secret-key";
 
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("your-client-id")
                .secret(SECRET_KEY)
                .authorizedGrantTypes("password", "refresh_token")
                .scopes("read", "write")
                .accessTokenValiditySeconds(3600)
                .refreshTokenValiditySeconds(86400);
    }
 
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore());
    }
 
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }
 
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SECRET_KEY);
        return converter;
    }
}

资源服务器配置:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
 
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/api/**").authenticated();
    }
}


  1. 令牌的有效期管理:令牌应具有适当的有效期,以限制其使用时间。授权服务器应定期检查和清理过期的令牌,并提供令牌刷新机制,使客户端能够获取新的令牌。


在Spring Cloud中,可以使用Spring Security OAuth2的功能来管理令牌的有效期:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
 
    // ...
 
    @Autowired
    private TokenStore tokenStore;
 
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore)
                .tokenEnhancer(tokenEnhancerChain());
    }
 
    @Bean
    public TokenEnhancerChain tokenEnhancerChain() {
        TokenEnhancerChain chain = new TokenEnhancerChain();
        chain.setTokenEnhancers(Arrays.asList(accessTokenConverter()));
        return chain;
    }
}


  1. 跨站请求伪造(CSRF)攻击:客户端应采取适当的CSRF防护措施,如使用随机生成的令牌进行请求验证,以防止恶意站点利用受信任的用户凭据进行攻击。


在Spring Cloud中,可以使用Spring Security的CSRF防护功能来防止CSRF攻击:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf()
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    }
}


  1. 频率限制和访问控制:授权服务器和资源服务器应实施适当的频率限制和访问控制策略,以防止恶意用户或恶意客户端对系统进行滥用和攻击。可以使用Spring Security的功能来实现频率限制和访问控制:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/api/**").hasAnyRole("USER", "ADMIN")
                .and()
                .addFilterBefore(rateLimitFilter(), UsernamePasswordAuthenticationFilter.class);
    }
 
    @Bean
    public RateLimitFilter rateLimitFilter() {
        return new RateLimitFilter();
    }
}

然后建立一个Filter来进行控制  这里使用的是OncePerRequestFilter:

public class RateLimitFilter extends OncePerRequestFilter {
 
    private static final int MAX_REQUESTS_PER_SECOND = 10;
 
    private final Map<String, Long> requestCounts = new ConcurrentHashMap<>();
 
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String clientId = getClientId(request);
        if (clientId != null) {
            if (exceedsRateLimit(clientId)) {
                response.sendError(HttpStatus.TOO_MANY_REQUESTS.value(), "Rate limit exceeded");
                return;
            } else {
                incrementRequestCount(clientId);
            }
        }
 
        filterChain.doFilter(request, response);
    }
 
    private String getClientId(HttpServletRequest request) {
        // 根据请求获取客户端ID
        // 例如,从请求头中获取或从请求参数中获取
        return request.getHeader("Client-Id");
    }
 
    private boolean exceedsRateLimit(String clientId) {
        long currentTimestamp = System.currentTimeMillis();
        requestCounts.entrySet().removeIf(entry -> entry.getValue() < currentTimestamp - 1000);
        return requestCounts.compute(clientId, (k, v) -> v == null ? 1 : v + 1) > MAX_REQUESTS_PER_SECOND;
    }
 
    private void incrementRequestCount(String clientId) {
        requestCounts.compute(clientId, (k, v) -> v == null ? 1 : v + 1);
    }
}
  1. 安全审计和监控:系统应具备安全审计和监控机制,记录和监测与令牌相关的活动,以及检测和响应潜在的安全事件。


这里可以使用Spring Boot Actuator和其他安全审计工具来实现安全审计和监控


首先,添加所需的依赖项到项目的pom.xml文件中:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

然后,在application.propertiesapplication.yml文件中配置安全审计和监控:

management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
management.endpoint.auditevents.enabled=true
management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-details: always
    auditevents:
      enabled: true

这样配置后,可以通过访问/actuator/auditevents端点来获取与令牌相关的审计事件信息。


3.5 OAuth2协议的扩展和变体:


OAuth2协议是一个灵活的协议,可以根据需要进行扩展和变体。以下是一些常见的OAuth2协议扩展和变体:


  1. OpenID Connect:OpenID Connect是在OAuth2协议基础上构建的身份验证协议,用于实现基于OAuth2的身份提供者功能,提供了用户身份验证和用户信息获取的能力。


  1. JWT(JSON Web Tokens):JWT是一种基于JSON的令牌格式,用于在OAuth2协议中表示令牌。JWT可用于在令牌中包含更多的声明信息,以便于验证和传递用户的身份信息。


  1. PKCE(Proof Key for Code Exchange):PKCE是一种用于增强授权码模式安全性的扩展,它使用随机生成的密钥来绑定授权码的使用,防止授权码被截获和重放攻击。


  1. Device Flow:设备流是一种适用于不具备浏览器和键盘的设备的授权流程,如智能电视、物联网设备等。它通过使用设备上的受限用户界面和用户代理进行授权交互。


  1. 自定义授权类型:根据特定的需求,可以扩展OAuth2协议以实现自定义的授权类型。这些自定义授权类型可以根据应用程序的要求定义新的授权流程和许可方式。


这里就不详细说了具体的选择根据实际业务而制定


4.Spring Security OAuth2的实现机制


4.1 Spring Security OAuth2的核心组件:


Spring Security OAuth2提供了一些核心组件来实现OAuth2的认证和授权机制。其中包括:


  • Authorization Server(授权服务器):负责颁发访问令牌(Access Token)和刷新令牌(Refresh Token),用于客户端进行认证和授权。
  • Resource Server(资源服务器):保护受限资源,需要访问令牌才能访问。
  • Client(客户端):代表用户或应用程序,向授权服务器请求访问令牌,并使用该令牌访问受限资源。
  • User(用户):最终的资源拥有者,通过客户端进行认证和授权。


这些组件一起工作,实现了OAuth2的认证和授权机制。下面我们将详细说明如何配置和使用这些组件。


4.2 配置Spring Security OAuth2:


首先,我们需要在Spring Boot项目中配置Spring Security OAuth2。以下是一个简单的配置示例:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    
    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient("client_id")
            .secret("client_secret")
            .authorizedGrantTypes("authorization_code", "refresh_token")
            .scopes("read", "write")
            .accessTokenValiditySeconds(3600)
            .refreshTokenValiditySeconds(86400);
    }
    
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager);
    }
}

在上述代码中,我们通过@EnableAuthorizationServer注解启用了授权服务器,并继承了AuthorizationServerConfigurerAdapter类来配置授权服务器。在configure方法中,我们配置了一个简单的客户端,包括客户端ID、密钥、授权类型、作用域以及访问令牌和刷新令牌的有效期。在configure方法中,我们还将authenticationManager注入到AuthorizationServerEndpointsConfigurer中,以便进行用户认证。


4.3 自定义授权服务器和资源服务器:


Spring Security OAuth2允许我们自定义授权服务器和资源服务器。我们可以通过扩展AuthorizationServerConfigurerAdapter和ResourceServerConfigurerAdapter类来实现自定义配置。


以下是一个简单的示例:

@Configuration
@EnableAuthorizationServer
public class CustomAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    // 自定义授权服务器的配置
}
 
@Configuration
@EnableResourceServer
public class CustomResourceServerConfig extends ResourceServerConfigurerAdapter {
    // 自定义资源服务器的配置
}

在上述代码中,我们分别扩展了AuthorizationServerConfigurerAdapter和ResourceServerConfigurerAdapter类,并使用@EnableAuthorizationServer和@EnableResourceServer注解启用了自定义的授权服务器和资源服务器。我们可以在相应的配置类中添加自定义的配置,例如定义访问规则、认证管理器等。


4.4 用户认证和授权的处理流程:


Spring Security OAuth2处理用户认证和授权的流程如下:


  1. 客户端向授权服务器发起认证请求,提供客户端ID、密钥、授权类型等信息。
// 客户端发起认证请求的代码示例
RestTemplate restTemplate = new RestTemplate();
 
// 构建认证请求参数
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("grant_type", "password");
params.add("username", "user");
params.add("password", "password");
params.add("client_id", "client_id");
params.add("client_secret", "client_secret");
 
// 发起认证请求
ResponseEntity<TokenResponse> response = restTemplate.postForEntity("http://localhost:8080/oauth/token", params, TokenResponse.class);
TokenResponse tokenResponse = response.getBody();
  1. 授权服务器验证客户端的身份和权限,并颁发访问令牌和刷新令牌。
// 配置授权服务器的代码示例
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
 
    @Autowired
    private AuthenticationManager authenticationManager;
 
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient("client_id")
            .secret("client_secret")
            .authorizedGrantTypes("password", "refresh_token")
            .scopes("read", "write")
            .accessTokenValiditySeconds(3600)
            .refreshTokenValiditySeconds(86400);
    }
 
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager);
    }
}
  1. 客户端使用访问令牌请求资源服务器获取受限资源。
// 客户端请求资源服务器的代码示例
RestTemplate restTemplate = new RestTemplate();
 
// 设置访问令牌
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + tokenResponse.getAccess_token());
HttpEntity<String> entity = new HttpEntity<>(headers);
 
// 请求受限资源
ResponseEntity<String> response = restTemplate.exchange("http://localhost:8080/api/resource", HttpMethod.GET, entity, String.class);
String resource = response.getBody();
  1. 资源服务器验证访问令牌的有效性和权限,并返回受限资源给客户端。
// 配置资源服务器的代码示例
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/api/resource").authenticated();
    }
}

整个流程中涉及到用户的认证、客户端的认证和授权服务器的认证和授权逻辑。Spring Security OAuth2提供了相应的过滤器和处理器来处理这些逻辑。


4.5 集成第三方认证和授权服务商:


在Spring Security OAuth2中,我们可以集成第三方认证和授权服务商,例如Google、Facebook、GitHub等。


5.实战案例 登录集成集成GitHub


1.配置application.properties文件,添加GitHub OAuth2相关配置:(这里记得要换成你们自己的账号之类的配置)

spring.security.oauth2.client.registration.github.client-id=your-client-id
spring.security.oauth2.client.registration.github.client-secret=your-client-secret
spring.security.oauth2.client.registration.github.scope=user:email
spring.security.oauth2.client.registration.github.redirect-uri=http://localhost:8080/login/oauth2/code/github
spring.security.oauth2.client.provider.github.authorization-uri=https://github.com/login/oauth/authorize
spring.security.oauth2.client.provider.github.token-uri=https://github.com/login/oauth/access_token
spring.security.oauth2.client.provider.github.user-info-uri=https://api.github.com/user

2.创建一个自定义的登录页面(login.html),包含GitHub登录按钮:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
    <h2>Login</h2>
    <a href="/login/github">Login with GitHub</a>
</body>
</html>

3.创建一个Controller处理登录请求和回调:

@Controller
public class LoginController {
 
    @GetMapping("/login")
    public String login() {
        return "login";
    }
    
    @GetMapping("/login/github")
    public RedirectView loginWithGithub() {
        return new RedirectView("/oauth2/authorization/github");
    }
    
    @GetMapping("/login/oauth2/code/github")
    public String handleGithubCallback(@RequestParam("code") String code) {
        // 处理GitHub回调逻辑
        return "redirect:/home";
    }
}

创建一个HomeController用于验证登录成功后的页面:

@Controller
public class HomeController {
 
    @GetMapping("/home")
    public String home() {
        return "home";
    }
    
}

创建一个WebSecurityConfigurerAdapter配置类,启用OAuth2登录:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/login").permitAll()
            .anyRequest().authenticated()
            .and()
            .oauth2Login();
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("user")
            .password("{noop}password")
            .roles("USER");
    }
    
}

这样就完成啦!然后运行应用程序,并访问http://localhost:8080/login,点击"Login with GitHub"按钮进行GitHub登录。

登录成功!


那我们就来看一个完整的使用SpringCloud整合Spring Security OAuth2实现微服务之间的安全通信的案例吧


我们将使用一个商城以及商家管理后台的业务部模块来讲解如何使用Spring Security OAuth2实现微服务之间的安全通信。


目前拿出来示例的是两个微服务:商城服务和商家管理后台服务。商城服务负责处理用户的购物流程,而商家管理后台服务用于管理商家的商品和订单信息。


创建多个微服务


创建商城服务和商家管理后台服务的Spring Boot项目。可以使用Spring Initializr(https://start.spring.io/)来快速生成项目骨架。


配置Spring Security OAuth2的客户端


在商城服务和商家管理后台服务的配置文件(例如application.properties或application.yml)中,添加以下配置:

spring:
  security:
    oauth2:
      client:
        registration:
          my-client: # 客户端ID,可以自定义
            client-id: <your-client-id>
            client-secret: <your-client-secret>
            authorization-grant-type: client_credentials
            scope: read, write
            provider: my-provider # 授权服务器名称,可以自定义
        provider:
          my-provider:
            token-uri: <authorization-server-token-uri>

请替换<your-client-id>、<your-client-secret>和<authorization-server-token-uri>为实际值。这些值将根据你的授权服务器的配置而有所不同。


步骤3:创建授权服务器


创建一个独立的授权服务器,用于颁发访问令牌和验证客户端。可以使用Spring Security OAuth2和Spring Boot来实现授权服务器。


授权服务器:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
 
    @Autowired
    private AuthenticationManager authenticationManager;
 
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("<your-client-id>")
                .secret("{noop}<your-client-secret>")
                .authorizedGrantTypes("client_credentials")
                .scopes("read", "write");
    }
 
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager);
    }
}

步骤4:配置微服务的授权服务器信息和访问令牌


在商城服务和商家管理后台服务的配置文件中,配置授权服务器的信息和访问令牌。

商城服务的配置文件示例:

spring:
  security:
    oauth2:
      client:
        registration:
          my-client:
            client-id: <your-client-id>
            client-secret: <your-client-secret>
            provider: my-provider
        provider:
          my-provider:
            token-uri: <authorization-server-token-uri>

商家管理后台服务的配置文件示例:

spring:
  security:
    oauth2:
      client:
        registration:
          my-client:
            client-id: <your-client-id>
            client-secret: <your-client-secret>
            provider: my-provider
        provider:
          my-provider:
            token-uri: <authorization-server-token-uri>

请替换<your-client-id>、<your-client-secret>和<authorization-server-token-uri>为您的实际值。


步骤5:配置微服务的安全规则


在商城服务和商家管理后台服务中,配置安全规则,包括访问规则和权限控制。


商城服务的安全配置示例:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/public/**").permitAll()
                .antMatchers("/api/**").authenticated()
                .and()
                .oauth2ResourceServer()
                .jwt();
    }
}

商家管理后台服务的安全配置示例:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/public/**").permitAll()
                .antMatchers("/api/**").authenticated()
                .and()
                .oauth2ResourceServer()
                .jwt();
    }
 
    @Bean
    public JwtDecoder jwtDecoder() {
        NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder) JwtDecoders.fromIssuerLocation("<authorization-server-issuer-uri>");
        // 配置JwtDecoder,包括验证签名等
        return jwtDecoder;
    }
}

请替换<authorization-server-issuer-uri>为你的授权服务器的颁发者URI。


上述代码示例将配置商家管理后台服务的安全规则。所有以/public/开头的请求将被允许无需身份验证,而以/api/开头的请求将需要进行身份验证。


通过添加JwtDecoder bean,我们可以配置JWT解码器,以便验证JWT令牌的签名等信息

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
 
@Configuration
public class SecurityConfig {
 
    @Bean
    public JwtDecoder jwtDecoder() {
        String issuerUri = "<authorization-server-issuer-uri>";
        NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(issuerUri + "/.well-known/jwks.json").build();
        // 配置JwtDecoder,包括验证签名等
        return jwtDecoder;
    }
}

请将<authorization-server-issuer-uri>替换为你的授权服务器的颁发者URI。


导入依赖

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-jose</artifactId>
    <version>5.x.x</version>
</dependency>


这样就实现了一个微服务系统当中的安全通信的保护啦

相关文章
|
3月前
|
负载均衡 算法 Java
Spring Cloud全解析:负载均衡算法
本文介绍了负载均衡的两种方式:集中式负载均衡和进程内负载均衡,以及常见的负载均衡算法,包括轮询、随机、源地址哈希、加权轮询、加权随机和最小连接数等方法,帮助读者更好地理解和应用负载均衡技术。
|
3月前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
471 37
|
3月前
|
XML 监控 Java
Spring Cloud全解析:熔断之Hystrix简介
Hystrix 是由 Netflix 开源的延迟和容错库,用于提高分布式系统的弹性。它通过断路器模式、资源隔离、服务降级及限流等机制防止服务雪崩。Hystrix 基于命令模式,通过 `HystrixCommand` 封装对外部依赖的调用逻辑。断路器能在依赖服务故障时快速返回备选响应,避免长时间等待。此外,Hystrix 还提供了监控功能,能够实时监控运行指标和配置变化。依赖管理方面,可通过 `@EnableHystrix` 启用 Hystrix 支持,并配置全局或局部的降级策略。结合 Feign 可实现客户端的服务降级。
190 23
|
2月前
|
缓存 资源调度 JavaScript
npx与npm的差异解析,以及包管理器yarn与Node版本管理工具nvm的使用方法详解
npx与npm的差异解析,以及包管理器yarn与Node版本管理工具nvm的使用方法详解
53 0
|
3月前
|
JSON API 数据格式
requests库中json参数与data参数使用方法的深入解析
选择 `data`或 `json`取决于你的具体需求,以及服务器端期望接收的数据格式。
277 2
|
4月前
|
Java 微服务 Spring
Spring Cloud全解析:配置中心之解决configserver单点问题
但是如果该configserver挂掉了,那就无法获取最新的配置了,微服务就出现了configserver的单点问题,那么如何避免configserver单点呢?
|
4月前
|
XML Java 数据格式
Spring Cloud全解析:注册中心之zookeeper注册中心
使用ZooKeeper作为Spring Cloud的注册中心无需单独部署服务器,直接利用ZooKeeper服务端功能。项目通过`spring-cloud-starter-zookeeper-discovery`依赖实现服务注册与发现。配置文件指定连接地址,如`localhost:2181`。启动应用后,服务自动注册到ZooKeeper的`/services`路径下,形成临时节点,包含服务实例信息。
349 3
|
4月前
|
消息中间件 Java RocketMQ
微服务架构师的福音:深度解析Spring Cloud RocketMQ,打造高可靠消息驱动系统的不二之选!
【8月更文挑战第29天】Spring Cloud RocketMQ结合了Spring Cloud生态与RocketMQ消息中间件的优势,简化了RocketMQ在微服务中的集成,使开发者能更专注业务逻辑。通过配置依赖和连接信息,可轻松搭建消息生产和消费流程,支持消息过滤、转换及分布式事务等功能,确保微服务间解耦的同时,提升了系统的稳定性和效率。掌握其应用,有助于构建复杂分布式系统。
71 0
|
XML Java 数据格式
Spring面试底层原理的那些问题,你是不是真的懂Spring?
1、什么是 Spring 框架?Spring 框架有哪些主要模块?Spring 框架是一个为 Java 应用程序的开发提供了综合、广泛的基础性支持的 Java 平台。Spring帮助开发者解决了开发中基础性的问题,使得开发人员可以专注于应用程序的开发。
1503 0
|
2月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
210 2

推荐镜像

更多