使用spring-security-oauth2作为client实现

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

本文主要讲一下如何使用spring security oauth2作为一个client来使用

四种模式

OAuth 2.0定义了四种授权方式。

  • 授权码模式(authorization code)
  • 简化模式(implicit)(client为浏览器/前端应用)
  • 密码模式(resource owner password credentials)(用户密码暴露给client端不安全)
  • 客户端模式(client credentials)(主要用于api认证,跟用户无关)

这里以authorization code模式为例

实现client的主要思路

  • 需要新建一个处理redirectUri的controller或者filter进行处理
  • 根据authentication code去请求token
  • 获取token之后将token与用户绑定
  • 之后就可以使用token去获取授权的资源

OAuth2RestTemplate(封装获取token方法)

对rest template的封装,为获取token等提供便捷方法
DefaultUserInfoRestTemplateFactory实例了OAuth2RestTemplate

DefaultUserInfoRestTemplateFactory

spring-boot-autoconfigure-1.5.9.RELEASE-sources.jar!/org/springframework/boot/autoconfigure/security/oauth2/resource/DefaultUserInfoRestTemplateFactory.java

    /**
     * Factory used to create the {@link OAuth2RestTemplate} used for extracting user info
     * during authentication if none is available.
     *
     * @author Dave Syer
     * @author Stephane Nicoll
     * @since 1.5.0
     */
    public class DefaultUserInfoRestTemplateFactory implements UserInfoRestTemplateFactory {

        private static final AuthorizationCodeResourceDetails DEFAULT_RESOURCE_DETAILS;

        static {
            AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails();
            details.setClientId("<N/A>");
            details.setUserAuthorizationUri("Not a URI because there is no client");
            details.setAccessTokenUri("Not a URI because there is no client");
            DEFAULT_RESOURCE_DETAILS = details;
        }

        private final List<UserInfoRestTemplateCustomizer> customizers;

        private final OAuth2ProtectedResourceDetails details;

        private final OAuth2ClientContext oauth2ClientContext;

        private OAuth2RestTemplate oauth2RestTemplate;

        public DefaultUserInfoRestTemplateFactory(
                ObjectProvider<List<UserInfoRestTemplateCustomizer>> customizers,
                ObjectProvider<OAuth2ProtectedResourceDetails> details,
                ObjectProvider<OAuth2ClientContext> oauth2ClientContext) {
            this.customizers = customizers.getIfAvailable();
            this.details = details.getIfAvailable();
            this.oauth2ClientContext = oauth2ClientContext.getIfAvailable();
        }

        @Override
        public OAuth2RestTemplate getUserInfoRestTemplate() {
            if (this.oauth2RestTemplate == null) {
                this.oauth2RestTemplate = createOAuth2RestTemplate(
                        this.details == null ? DEFAULT_RESOURCE_DETAILS : this.details);
                this.oauth2RestTemplate.getInterceptors()
                        .add(new AcceptJsonRequestInterceptor());
                AuthorizationCodeAccessTokenProvider accessTokenProvider = new AuthorizationCodeAccessTokenProvider();
                accessTokenProvider.setTokenRequestEnhancer(new AcceptJsonRequestEnhancer());
                this.oauth2RestTemplate.setAccessTokenProvider(accessTokenProvider);
                if (!CollectionUtils.isEmpty(this.customizers)) {
                    AnnotationAwareOrderComparator.sort(this.customizers);
                    for (UserInfoRestTemplateCustomizer customizer : this.customizers) {
                        customizer.customize(this.oauth2RestTemplate);
                    }
                }
            }
            return this.oauth2RestTemplate;
        }

        private OAuth2RestTemplate createOAuth2RestTemplate(
                OAuth2ProtectedResourceDetails details) {
            if (this.oauth2ClientContext == null) {
                return new OAuth2RestTemplate(details);
            }
            return new OAuth2RestTemplate(details, this.oauth2ClientContext);
        }

    }

这个提供了OAuth2RestTemplate

ResourceServerTokenServicesConfiguration

spring-boot-autoconfigure-1.5.9.RELEASE-sources.jar!/org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfiguration.java

    /**
     * Configuration for an OAuth2 resource server.
     *
     * @author Dave Syer
     * @author Madhura Bhave
     * @author Eddú Meléndez
     * @since 1.3.0
     */
    @Configuration
    @ConditionalOnMissingBean(AuthorizationServerEndpointsConfiguration.class)
    public class ResourceServerTokenServicesConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public UserInfoRestTemplateFactory userInfoRestTemplateFactory(
                ObjectProvider<List<UserInfoRestTemplateCustomizer>> customizers,
                ObjectProvider<OAuth2ProtectedResourceDetails> details,
                ObjectProvider<OAuth2ClientContext> oauth2ClientContext) {
            return new DefaultUserInfoRestTemplateFactory(customizers, details,
                    oauth2ClientContext);
        }

        //......
    }    

而DefaultUserInfoRestTemplateFactory主要是在ResourceServerTokenServicesConfiguration配置中创建的
这个是给resource server用的,因而client要使用的话,需要自己创建

redirectUri的处理(OAuth2ClientAuthenticationProcessingFilter)

spring security oauth2 照样提供了便利的类可供处理:
spring-security-oauth2-2.0.14.RELEASE-sources.jar!/org/springframework/security/oauth2/client/filter/OAuth2ClientAuthenticationProcessingFilter.java

    /**
     * An OAuth2 client filter that can be used to acquire an OAuth2 access token from an authorization server, and load an
     * authentication object into the SecurityContext
     * 
     * @author Vidya Valmikinathan
     * 
     */
    public class OAuth2ClientAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {

        public OAuth2RestOperations restTemplate;

        private ResourceServerTokenServices tokenServices;

        private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new OAuth2AuthenticationDetailsSource();

        private ApplicationEventPublisher eventPublisher;

        public OAuth2ClientAuthenticationProcessingFilter(String defaultFilterProcessesUrl) {
            super(defaultFilterProcessesUrl);
            setAuthenticationManager(new NoopAuthenticationManager());
            setAuthenticationDetailsSource(authenticationDetailsSource);
        }
        //......
    }    

它的构造器需要传入defaultFilterProcessesUrl,用于指定这个filter拦截哪个url。
它依赖OAuth2RestTemplate来获取token
还依赖ResourceServerTokenServices进行校验token

oauth client config

经过上面的分析,这个config主要是配置3个

  • OAuth2RestTemplate(获取token)
  • ResourceServerTokenServices(校验token)
  • OAuth2ClientAuthenticationProcessingFilter(拦截redirectUri,根据authentication code获取token,依赖前面两个对象)
    @Configuration
    @EnableOAuth2Client
    public class Oauth2ClientConfig {

        @Bean
        public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext context, OAuth2ProtectedResourceDetails details) {
            OAuth2RestTemplate template = new OAuth2RestTemplate(details, context);

            AuthorizationCodeAccessTokenProvider authCodeProvider = new AuthorizationCodeAccessTokenProvider();
            authCodeProvider.setStateMandatory(false);
            AccessTokenProviderChain provider = new AccessTokenProviderChain(
                    Arrays.asList(authCodeProvider));
            template.setAccessTokenProvider(provider);
        }

        /**
         * 注册处理redirect uri的filter
         * @param oauth2RestTemplate
         * @param tokenService
         * @return
         */
        @Bean
        public OAuth2ClientAuthenticationProcessingFilter oauth2ClientAuthenticationProcessingFilter(
                OAuth2RestTemplate oauth2RestTemplate,
                RemoteTokenServices tokenService) {
            OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(redirectUri);
            filter.setRestTemplate(oauth2RestTemplate);
            filter.setTokenServices(tokenService);

            //设置回调成功的页面
            filter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler() {
                public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                    this.setDefaultTargetUrl("/home");
                    super.onAuthenticationSuccess(request, response, authentication);
                }
            });
            return filter;
        }

        /**
         * 注册check token服务
         * @param details
         * @return
         */
        @Bean
        public RemoteTokenServices tokenService(OAuth2ProtectedResourceDetails details) {
            RemoteTokenServices tokenService = new RemoteTokenServices();
            tokenService.setCheckTokenEndpointUrl(checkTokenUrl);
            tokenService.setClientId(details.getClientId());
            tokenService.setClientSecret(details.getClientSecret());
            return tokenService;
        }
    }

security config

上面定义了OAuth2ClientAuthenticationProcessingFilter,还有最重要的一步,就是配置filter的顺序,如果配置不当则前功尽弃。

这里需要配置在BasicAuthenticationFilter之前

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {

        @Autowired
        OAuth2ClientAuthenticationProcessingFilter oauth2ClientAuthenticationProcessingFilter;

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests().anyRequest().permitAll()
            .and()
            .addFilterBefore(oauth2ClientAuthenticationProcessingFilter,BasicAuthenticationFilter.class)
            .csrf().disable();
        }
    }

异常

    Possible CSRF detected - state parameter was required but no state could be found

有的是说本地开发,auth server与client都是localhost,造成JSESSIONID相互影响问题。可以通过配置client的context-path或者session名称来解决

这里配置了session

    server:
      port: 8081
      session:
        cookie:
          name: OAUTH2SESSION

不过貌似没解决,最后先临时关闭AuthorizationCodeAccessTokenProvider的stateMandatory属性

client相关yml配置

    security:
      oauth2:
        client:
          clientId: demoApp
          clientSecret: demoAppSecret
          accessTokenUri: ${TOKEN_URL:http://localhost:8080}/oauth/token
          userAuthorizationUri: ${USER_AUTH_URL:http://localhost:8080}/oauth/authorize
          pre-established-redirect-uri: http://localhost:8081/callback

验证

    http://localhost:8080/oauth/authorize?response_type=code&client_id=demoApp&redirect_uri=http://localhost:8081/callback

之后就是登陆,然后授权,然后就成功回调,然后跳转到设置的/home

回调之后,会将token与当前session绑定,之后利用OAuth2RestTemplate可以透明访问授权资源

    @RequestMapping("")
    @RestController
    public class DemoController {

        @Autowired
        OAuth2RestTemplate oAuth2RestTemplate;

        @Value("${client.resourceServerUrl}")
        String resourceServerUrl;

        @GetMapping("/demo/{id}")
        public String getDemoAuthResource(@PathVariable Long id){
            ResponseEntity<String> responseEntity = oAuth2RestTemplate.getForEntity(resourceServerUrl+"/demo/"+id, String.class);
            return responseEntity.getBody();
        }
    }

这样就大功告成了。

doc


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » 使用spring-security-oauth2作为client实现

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏