使用Spring Security + WSO2 Identity Server的OAuth 2.0

我正在开发一个Web应用程序来公开由OAuth 2.0保护的许多RESTful服务。 这是计划的架构:

1- OAuth授权提供商: WSO2身份服务器(IS)

2- OAuth资源服务器:使用以下技术的Java Web应用程序:

  • Jersey(实现和公开Web服务)
  • Spring Security(实现OAuth资源服务器部分)

我已经看到了几个关于如何使用WSO2 IS作为授权服务器+ WSO2 ESB作为资源服务器来保护RESTful服务的示例( ex1 , ex2 , ex3等)。 这不是我需要的。

遗憾的是,授权服务器和资源服务器之间的交互超出了OAuth2 RFC的范围。 所以,我找不到它应该是什么样子。

这是我的问题:

  • 如何配置spring security作为资源服务器来validation外部OAuth提供程序(例如WSO2 IS)发出的访问令牌?
  • 资源服务器应该如何识别给定访问令牌的范围?
  • 如何识别来自WSO2 IS的访问令牌的资源所有者?

谢谢

做了一些研究后,我想出了如何做到这一点。 该解决方案分为两个主要部分: WSO2 IS配置资源服务器配置

基本情况如下:

1-客户端(例如移动应用程序)通过向资源服务器(在我的情况下为Java Web应用程序)发送请求来使用安全资源(例如,Web服务)。

2-资源服务器validation请求中的“授权”标头并提取访问令牌。

3-资源服务器通过将访问令牌发送到授权服务器(WSO2 IS)来validation访问令牌。

4-授权服务器响应validation响应。

5-资源服务器validation响应并决定是否授予或拒绝对所请求资源的访问权限。

在我的演示中,我使用了WSO2 IS 5.0.0和Spring security 3.1.0。


1- WSO2 IS配置

WSO2 IS将充当授权服务器 。 因此,它应配置为支持OAuth 2.0。 为此,应添加和配置新的服务提供商,如下所示:

(a)登录WSO2 IS管理控制台。

(b)添加新的服务提供商并给它一个名称和描述。

服务提供商配置:截图1

(c)在入站身份validation配置下 >> OAuth / OpenID Connect配置 >>单击“ 配置”

(d)配置OAuth 2.0提供程序,如下面的屏幕截图所示,然后单击“ 添加” 。 我们需要密码授权类型,该类型映射到资源所有者密码凭据授予类型。 它最适合我的情况(保护Web服务)。

服务提供商配置:截图2

(e)在OAuth / OpenID Connect配置下 ,您将找到生成的OAuth客户端密钥OAuth客户端密钥 。 它们与用户名,密码和范围一起使用以生成访问令牌。


2-资源服务器配置

如前所述,演示Java Web应用程序将同时充当资源服务器和客户端。 要充当资源服务器,Spring安全性需要知道如何validation访问令牌。 因此,应提供令牌服务实现。

(a)配置spring作为资源服务器。 这是一个示例配置:

            

这里, 配置了使用令牌服务实现TokenServiceWSO2资源服务器资源服务器标记实际上已转换为安全筛选器。 拦截模式被添加到“/ services / **”,资源服务器filter被添加到链中。

(b)实施OAuth 2.0令牌服务ResourceServerTokenServices 。 该实现将访问令牌作为输入,将其传递给WSO2 IS公开的OAuth2TokenValidationService服务,validation响应并返回包含有关令牌发行者,有效性,范围,相应JWT令牌的基本数据的处理对象,……

 public class TokenServiceWSO2 implements ResourceServerTokenServices { @Autowired TokenValidatorWSO2 tokenValidatorWSO2; public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException { try { TokenValidationResponse validationResponse = tokenValidatorWSO2.validateAccessToken(accessToken); OAuth2Request oAuth2Request = new OAuth2Request(null, null, null, true, validationResponse.getScope(), null, null, null,null); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(validationResponse.getAuthorizedUserIdentifier(), null, null); OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication); return oAuth2Authentication; } catch (ApplicationException ex) { // Handle exception } } public OAuth2AccessToken readAccessToken(String accessToken) { // TODO Add implementation } } 

TokenValidatorWSO2类实现调用WSO2 IS的Web服务OAuth2TokenValidationService的逻辑

 @Component public class TokenValidatorWSO2 implements OAuth2TokenValidator{ private static final Logger logger = Logger.getLogger(TokenValidatorWSO2.class); @Value("${server_url}") private String serverUrl; @Value("${validation_service_name}") private String validationServiceName; @Value("${comsumer_key}") private String consumerKey; @Value("${admin_username}") private String adminUsername; @Value("${admin_password}") private String adminPassword; private OAuth2TokenValidationServiceStub stub; private static final int TIMEOUT_IN_MILLIS = 15 * 60 * 1000; public TokenValidationResponse validateAccessToken(String accessToken) throws ApplicationException { logger.debug("validateAccessToken(String) - start"); if(stub == null) { initializeValidationService(); } OAuth2TokenValidationRequestDTO oauthRequest; TokenValidationResponse validationResponse; OAuth2TokenValidationRequestDTO_OAuth2AccessToken oAuth2AccessToken; try { oauthRequest = new OAuth2TokenValidationRequestDTO(); oAuth2AccessToken = new OAuth2TokenValidationRequestDTO_OAuth2AccessToken(); oAuth2AccessToken.setIdentifier(accessToken); oAuth2AccessToken.setTokenType("bearer"); oauthRequest.setAccessToken(oAuth2AccessToken); OAuth2TokenValidationResponseDTO response = stub.validate(oauthRequest); if(!response.getValid()) { throw new ApplicationException("Invalid access token"); } validationResponse = new TokenValidationResponse(); validationResponse.setAuthorizedUserIdentifier(response.getAuthorizedUser()); validationResponse.setJwtToken(response.getAuthorizationContextToken().getTokenString()); validationResponse.setScope(new LinkedHashSet(Arrays.asList(response.getScope()))); validationResponse.setValid(response.getValid()); } catch(Exception ex) { logger.error("validateAccessToken() - Error when validating WSO2 token, Exception: {}", ex); } logger.debug("validateAccessToken(String) - end"); return validationResponse; } private void initializeValidationService() throws ApplicationException { try { String serviceURL = serverUrl + validationServiceName; stub = new OAuth2TokenValidationServiceStub(null, serviceURL); CarbonUtils.setBasicAccessSecurityHeaders(adminUsername, adminPassword, true, stub._getServiceClient()); ServiceClient client = stub._getServiceClient(); Options options = client.getOptions(); options.setTimeOutInMilliSeconds(TIMEOUT_IN_MILLIS); options.setProperty(HTTPConstants.SO_TIMEOUT, TIMEOUT_IN_MILLIS); options.setProperty(HTTPConstants.CONNECTION_TIMEOUT, TIMEOUT_IN_MILLIS); options.setCallTransportCleanup(true); options.setManageSession(true); } catch(AxisFault ex) { // Handle exception } } } 

TokenValidationResponse类保存令牌validation响应中返回的基本数据。

 public class TokenValidationResponse { private String jwtToken; private boolean valid; private Set scope; private String authorizedUserIdentifier; public String getJwtToken() { return jwtToken; } public void setJwtToken(String jwtToken) { this.jwtToken = jwtToken; } public boolean isValid() { return valid; } public void setValid(boolean valid) { this.valid = valid; } public Set getScope() { return scope; } public void setScope(Set scope) { this.scope = scope; } public String getAuthorizedUserIdentifier() { return authorizedUserIdentifier; } public void setAuthorizedUserIdentifier(String authorizedUserIdentifier) { this.authorizedUserIdentifier = authorizedUserIdentifier; } } 

3-客户端应用配置

最后一步是配置要受OAuth 2.0保护的资源。 基本上,使用根URL路径“/ services / **”配置要保护的Web服务。 在我的演示中,我使用了Jersey。


4-测试客户端应用程序

最后一步是使用安全的Web服务。 这是通过向请求添加Authorization标头来实现的,其值为“ “,例如” bearer 7fbd71c5b28fdf0bdb922b07915c4d5 “。


PS所描述的样本仅用于澄清目的。 它可能缺少一些实现,exception处理,…请进一步查询评论。