我尝试将授权代码流配置为客户端。就流程而言。我重定向到登录页面。 oauth2 服务器给了我一个授权码,我可以用这个码来交换访问 token 。
但我无法正确完成最后一步:回到原始资源。
这是我的安全配置:
@Configuration
@EnableWebSecurity
@EnableOAuth2Client
public class SecureConfig extends WebSecurityConfigurerAdapter {
@Autowired
OAuth2ClientContext oauth2ClientContext;
@Value("${openId.userinfo}")
private String userInfoUri;
@Value("${openId.clientId}")
private String clientId;
@Value("${openId.clientSecret}")
private String clientSecret;
@Value("${openId.accessTokenUri}")
private String accessTokenUri;
@Value("${openId.userAuthorizationUri}")
private String userAuthorizationUri;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.addFilterAfter(ssoFilter(), BasicAuthenticationFilter.class);
}
private OAuth2ClientAuthenticationProcessingFilter ssoFilter() {
OAuth2ClientAuthenticationProcessingFilter openIDFilter = new OAuth2ClientAuthenticationProcessingFilter("/resource/**");
openIDFilter.setRestTemplate(restTemplate());
UserInfoTokenServices tokenServices = new UserInfoTokenServices(userInfoUri, clientId);
tokenServices.setRestTemplate(restTemplate());
openIDFilter.setTokenServices(tokenServices);
return openIDFilter;
}
@Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public OAuth2RestTemplate restTemplate() {
return new OAuth2RestTemplate(protectedResourceDetails(), oauth2ClientContext);
}
@Bean
public FilterRegistrationBean oauth2ClientFilterRegistration(
OAuth2ClientContextFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
@Bean
public OAuth2ProtectedResourceDetails protectedResourceDetails() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
details.setClientId(clientId);
details.setClientSecret(clientSecret);
details.setAccessTokenUri(accessTokenUri);
details.setUserAuthorizationUri(userAuthorizationUri);
details.setScope(Arrays.asList("read"));
details.setUseCurrentUri(true);
return details;
}
}
这是我的 Controller :
@Controller
@RequestMapping("/resource")
public class TestController {
@RequestMapping(value = "/test", method = {RequestMethod.GET, RequestMethod.POST})
@ResponseStatus(code = HttpStatus.OK)
public void test(){
System.out.println("hello world");
}
}
在最后一步,spring 将我重定向到我的基本网址:
我找到了 this forum post
它建议将请求保存在
RequestCache
中.但是这篇文章大约有 6 年的历史了,也许 spring 在此期间提供了一个更优雅的解决方案?编辑:
这是我的依赖项:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
</dependencies>
最佳答案
这是一种解决方法,我不喜欢它。如果有人找到更好的解决方案,我将不胜感激。
为此,我将保持实现简单和肮脏。
首先我实现了 UpdateSavedRequestFilter
在 requestCache 中保存请求:
public class UpdateSavedRequestFilter extends OncePerRequestFilter {
private RequestCache requestCache = new HttpSessionRequestCache();
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String queryString = request.getQueryString();
if(!StringUtils.contains(queryString, "code") && authentication == null) {
requestCache.saveRequest(request, response);
}
filterChain.doFilter(request, response);
}
}
它没有按预期工作,我被重定向到“/resource/test”,但身份验证过程再次被触发。所以我已经实现了我自己的
Oauth2Filter
.它没什么用,我主要从 http://www.baeldung.com/spring-security-openid-connect 复制代码.我唯一的一点是对 doFilter
的扩展调用 requiresAuthentication
的方法检查用户是否已经通过身份验证public class OAuth2Filter extends AbstractAuthenticationProcessingFilter {
public OAuth2RestOperations restTemplate;
private UserInfoTokenServices tokenServices;
public OAuth2Filter(String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
setAuthenticationManager(new NoopAuthenticationManager());
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
setAuthenticationSuccessHandler(successHandler);
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
if (requiresAuthentication()) {
super.doFilter(req, res, chain);
} else {
chain.doFilter(req, res);
}
}
@Override
public Authentication attemptAuthentication(
HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException{
OAuth2AccessToken accessToken;
try {
accessToken = restTemplate.getAccessToken();
} catch (OAuth2Exception e) {
throw new BadCredentialsException("Could not obtain access token", e);
}
try {
OAuth2Authentication result = tokenServices.loadAuthentication(accessToken.getValue());
if (authenticationDetailsSource!=null) {
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, accessToken.getValue());
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, accessToken.getTokenType());
result.setDetails(authenticationDetailsSource.buildDetails(request));
}
publish(new AuthenticationSuccessEvent(result));
return result;
} catch (InvalidTokenException e) {
throw new BadCredentialsException("Could not obtain user details from token", e);
}
}
private void publish(ApplicationEvent event) {
if (eventPublisher!=null) {
eventPublisher.publishEvent(event);
}
}
private static class NoopAuthenticationManager implements AuthenticationManager {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
throw new UnsupportedOperationException("No authentication should be done with this AuthenticationManager");
}
}
private boolean requiresAuthentication() {
Authentication currentUser = SecurityContextHolder.getContext()
.getAuthentication();
if (currentUser == null) {
return true;
}
OAuth2AccessToken accessToken = restTemplate.getAccessToken();
if (accessToken == null) {
return true;
}
return accessToken.isExpired();
}
public void setRestTemplate(OAuth2RestOperations restTemplate) {
this.restTemplate = restTemplate;
}
public void setTokenServices(UserInfoTokenServices tokenServices) {
this.tokenServices = tokenServices;
}
}
这是我其余的 conig 类(class):
@Configuration
@EnableWebSecurity
public class SecureConfig extends WebSecurityConfigurerAdapter {
@Autowired
private OAuth2RestTemplate restTemplate;
@Value("${openId.userinfo}")
private String userInfoUri;
@Value("${openId.clientId}")
private String clientId;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.addFilterAfter(new OAuth2ClientContextFilter(), AbstractPreAuthenticatedProcessingFilter.class)
.addFilterAfter(openIdConnectFilter(), OAuth2ClientContextFilter.class)
.addFilterBefore(new UpdateSavedRequestFilter(), OAuth2Filter.class);
}
@Bean
public OAuth2Filter openIdConnectFilter() {
OAuth2Filter filter = new OAuth2Filter("/resource/**");
filter.setRestTemplate(restTemplate);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(userInfoUri, clientId);
tokenServices.setRestTemplate(restTemplate);
filter.setTokenServices(tokenServices);
return filter;
}
}
@Configuration
@EnableOAuth2Client
public class OpenIdConnectConfig {
@Value("${openId.userinfo}")
private String userInfoUri;
@Value("${openId.clientId}")
private String clientId;
@Value("${openId.clientSecret}")
private String clientSecret;
@Value("${openId.accessTokenUri}")
private String accessTokenUri;
@Value("${openId.userAuthorizationUri}")
private String userAuthorizationUri;
@Bean
public OAuth2ProtectedResourceDetails protectedResourceDetails() {
AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
details.setClientId(clientId);
details.setClientSecret(clientSecret);
details.setAccessTokenUri(accessTokenUri);
details.setUserAuthorizationUri(userAuthorizationUri);
details.setScope(Arrays.asList("read"));
details.setUseCurrentUri(true);
return details;
}
@Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public OAuth2RestTemplate restTemplate(OAuth2ClientContext clientContext) {
OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(protectedResourceDetails(), clientContext);
return oAuth2RestTemplate;
}
}
关于java - Spring oauth2 不重定向到原始 url,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49727551/