美好的一天,
我已经设置了一个实施SSO& amp;的实例。 API网关模式(类似于此处描述的https://spring.io/guides/tutorials/spring-security-and-angular-js/#_the_api_gateway_pattern_angular_js_and_spring_security_part_iv)。
系统由独立的服务器组件组成:AUTH-SERVER,API-GATEWAY,SERVICE-DISCOVERY,RESOURCE / UI SERVER。
在API-GATEWAY(使用Spring Boot @EnableZuulProxy实现 @ EnableOAuth2Sso)我已经使用JWT配置了多个OAuth提供程序,包括我自己的OAuth服务器:
security:
oauth2:
client:
accessTokenUri: http://localhost:9999/uaa/oauth/token
userAuthorizationUri: http://localhost:9999/uaa/oauth/authorize
clientId: acme
clientSecret: acmesecret
redirectUri: http://localhost:9000/login
resource:
jwt:
key-value: |
-----BEGIN PUBLIC KEY-----
...public-key...
-----END PUBLIC KEY-----
facebook:
client:
clientId: 233668646673605
clientSecret: 33b17e044ee6a4fa383f46ec6e28ea1d
accessTokenUri: https://graph.facebook.com/oauth/access_token
userAuthorizationUri: https://www.facebook.com/dialog/oauth
tokenName: oauth_token
authenticationScheme: query
clientAuthenticationScheme: form
redirectUri: http://localhost:8080
resource:
userInfoUri: https://graph.facebook.com/me
github:
client:
clientId: bd1c0a783ccdd1c9b9e4
clientSecret: 1a9030fbca47a5b2c28e92f19050bb77824b5ad1
accessTokenUri: https://github.com/login/oauth/access_token
userAuthorizationUri: https://github.com/login/oauth/authorize
clientAuthenticationScheme: form
resource:
userInfoUri: https://api.github.com/user
google:
client:
clientId: 1091750269931-152sv64o8a0vd5hg8v2lp92qd2d4i00r.apps.googleusercontent.com
clientSecret: n4I4MRNLKMdv603SU95Ic9lJ
accessTokenUri: https://www.googleapis.com/oauth2/v3/token
userAuthorizationUri: https://accounts.google.com/o/oauth2/auth
authenticationScheme: query
redirectUri: http://localhost:9000/login/google
scope:
- email
- profile
resource:
userInfoUri: https://www.googleapis.com/oauth2/v2/userinfo
Java Config:
package com.devdream.cloud.apigateway;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoRestTemplateCustomizer;
import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter;
import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.csrf.CsrfToken;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.CompositeFilter;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.WebUtils;
@SpringBootApplication
@EnableZuulProxy
@EnableOAuth2Sso
public class APIGatewayApplication extends WebSecurityConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(APIGatewayApplication.class, args);
}
@Autowired
OAuth2ClientContext oauth2ClientContext;
@Override
public void configure(HttpSecurity http) throws Exception {
http//
.logout()
//
.and()
//
.antMatcher("/**")
//
.authorizeRequests()
//
.antMatchers("/index.html", "/home.html", "/login", "/stomp/**")
.permitAll()
//
.anyRequest()
.authenticated()
//
.and()
//
.csrf()
//
.csrfTokenRepository(csrfTokenRepository()).and()
.addFilterAfter(csrfHeaderFilter(), CsrfFilter.class).headers()
.frameOptions().sameOrigin()//
.and()//
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
}
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request
.getAttribute(CsrfToken.class.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null
&& !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
};
}
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
repository.setParameterName("X-XSRF-TOKEN");
return repository;
}
private Filter ssoFilter() {
CompositeFilter filter = new CompositeFilter();
List<Filter> filters = new ArrayList<>();
filters.add(ssoFilter(facebook(), "/login/facebook"));
filters.add(ssoFilter(github(), "/login/github"));
filters.add(ssoFilter(google(), "/login/google"));
filter.setFilters(filters);
return filter;
}
private Filter ssoFilter(ClientResources client, String path) {
OAuth2ClientAuthenticationProcessingFilter oAuth2Filter = new OAuth2ClientAuthenticationProcessingFilter(
path);
OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(
client.getClient(), oauth2ClientContext);
oAuth2Filter.setRestTemplate(oAuth2RestTemplate);
oAuth2Filter.setTokenServices(new UserInfoTokenServices(client
.getResource().getUserInfoUri(), client.getClient()
.getClientId()));
return oAuth2Filter;
}
@Bean
@ConfigurationProperties("github")
ClientResources github() {
return new ClientResources();
}
@Bean
@ConfigurationProperties("facebook")
ClientResources facebook() {
return new ClientResources();
}
@Bean
@ConfigurationProperties("google")
ClientResources google() {
return new ClientResources();
}
@Bean
public FilterRegistrationBean oauth2ClientFilterRegistration(
OAuth2ClientContextFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
}
当未经身份验证的请求发送到网关时,请求会按预期重定向到AUTH-SERVER,这里我提供了使用我的AUTH-SERVER以及上面配置的社交选项登录的选项,提供了一个基本上使用户返回API-GATEWAY,以便由上面配置的相关OAuth过滤器路径拦截。发布JWT令牌的我的AUTH-SERVER按预期工作,为我的资源数据提供服务。 ui但是当我成功使用Google进行身份验证时,我从资源/ ui服务器获得以下响应:
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<oauth>
<error_description>Cannot convert access token to JSON</error_description>
<error>invalid_token</error>
</oauth>
然后我意识到这可能是由于资源服务器的OAuth配置?
security:
oauth2:
client:
client-id: acme
client-secret: acmesecret
resource:
jwt:
key-value: |
-----BEGIN PUBLIC KEY-----
...public-key...
-----END PUBLIC KEY-----
资源服务器如何知道解码外部OAuth提供程序发送的令牌?可以在资源服务器中配置多个OAuth2客户端吗?我的想法在这里有缺陷吗?
在调试对资源服务器的请求后,我在org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter类中发现了从Google发送的令牌值:
ya29..xwLBw5mz3XoTo-xuaSGbwhuE3_wqtAwL8tP7sGe5wMRvChk6pxeH8CpPnPg83OlbnA
令牌中似乎没有有效负载?
我还看到正在使用的验证程序正在使用上面配置的jwt键值。
如何配置多个资源服务器oauth资源&amp;资源服务器如何知道使用哪一个?