java - Spring oauth2 不重定向到原始 url

标签 java spring oauth-2.0 spring-security-oauth2

我尝试将授权代码流配置为客户端。就流程而言。我重定向到登录页面。 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 将我重定向到我的基本网址:
enter image description here

我找到了 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/

相关文章:

java - BigInteger 内存泄漏导致 Java 中的堆栈溢出

java - 休息网络服务 : How to accept a list of parameter

java - JUnit、@ControllerAdvice 以及 Kotlin 中缺乏检查异常

spring-security - 在 Spring Resource Server 中采用来自 JWT 的权限

java - 如何从 Java 读取 proto3 自定义选项

Java:将字符串中的所有字符代码移动一个常量值

java - 如何为整个Spring Boot应用程序生成 token

javascript - 从 jquery 调用 spring 操作,无需使用 ajax 来实现多个按钮(例如 View 编辑等)

asp.net - 使用 asp.net Identity 的 WebAPI2 中的多个 token 生命周期

python - 如何使用 oauth2 为网站构建 Python 爬虫