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协议时,需要考虑以下安全性问题和采取相应的防护措施:
- 令牌的安全传输:令牌在客户端和服务器之间传输时应进行安全加密,以防止令牌被拦截和篡改。可以使用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(); } }
- 令牌的保密性:令牌应仅由授权服务器和资源服务器持有,并且不应通过客户端或其他不受信任的渠道传播。客户端应采取适当的安全措施,如存储令牌时进行加密处理。
在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(); } }
- 令牌的有效期管理:令牌应具有适当的有效期,以限制其使用时间。授权服务器应定期检查和清理过期的令牌,并提供令牌刷新机制,使客户端能够获取新的令牌。
在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; } }
- 跨站请求伪造(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()); } }
- 频率限制和访问控制:授权服务器和资源服务器应实施适当的频率限制和访问控制策略,以防止恶意用户或恶意客户端对系统进行滥用和攻击。可以使用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); } }
- 安全审计和监控:系统应具备安全审计和监控机制,记录和监测与令牌相关的活动,以及检测和响应潜在的安全事件。
这里可以使用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.properties
或application.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协议扩展和变体:
- OpenID Connect:OpenID Connect是在OAuth2协议基础上构建的身份验证协议,用于实现基于OAuth2的身份提供者功能,提供了用户身份验证和用户信息获取的能力。
- JWT(JSON Web Tokens):JWT是一种基于JSON的令牌格式,用于在OAuth2协议中表示令牌。JWT可用于在令牌中包含更多的声明信息,以便于验证和传递用户的身份信息。
- PKCE(Proof Key for Code Exchange):PKCE是一种用于增强授权码模式安全性的扩展,它使用随机生成的密钥来绑定授权码的使用,防止授权码被截获和重放攻击。
- Device Flow:设备流是一种适用于不具备浏览器和键盘的设备的授权流程,如智能电视、物联网设备等。它通过使用设备上的受限用户界面和用户代理进行授权交互。
- 自定义授权类型:根据特定的需求,可以扩展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处理用户认证和授权的流程如下:
- 客户端向授权服务器发起认证请求,提供客户端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();
- 授权服务器验证客户端的身份和权限,并颁发访问令牌和刷新令牌。
// 配置授权服务器的代码示例 @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); } }
- 客户端使用访问令牌请求资源服务器获取受限资源。
// 客户端请求资源服务器的代码示例 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();
- 资源服务器验证访问令牌的有效性和权限,并返回受限资源给客户端。
// 配置资源服务器的代码示例 @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>