spring security OAuth2.0之客户端Client的实现
项目代码:https://github.com/hankuikuide/microservice-spring-security-oauth2
网上多数的项目客户端都是采用纯js写,或用postman发请求,和实际项目的应用还是有差距的,这里也是采用spring boot的实现。 主要功能在于:
- 使用授权码模式进行认证。
- 使用OAuth2RestTemplate发送请求给认证服务器和资源服务器,
- 结合Feign实现loadbalance.
先进行security的配置:
@Configuration @EnableOAuth2Sso public class UiSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired OAuth2ClientAuthenticationProcessingFilter oauth2ClientAuthenticationProcessingFilter; @Override public void configure(HttpSecurity http) throws Exception { http.antMatcher("/**") .authorizeRequests() .antMatchers("/", "/login**") .permitAll().anyRequest().authenticated() .and() .addFilterBefore(oauth2ClientAuthenticationProcessingFilter,BasicAuthenticationFilter.class) .csrf().disable(); } }
在BasicAuthenticationFilter之前添加了一个过滤器。
客户端中最关键的代码,如下:
核心功能是
1. 注册OAuth2RestTemplate,
2.注册处理redirect uri的filter.也就是上面说的过滤器。
3. 注册check token服务
有了这个类,使用授权码模式,就可以把登录成功后的授权码接收到,并自动发给认证服务器请求token,并在后续的请求中自动添加token了。
@Configuration public class Oauth2ClientConfig { private String redirectUri ="http://localhost:9001/login"; private String checkTokenUrl ="http://localhost:9002/auth/oauth/check_token"; @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); return template; } /** * 注册处理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() { @Override 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; } }
再有虽然我们这里配置了OAuth2RestTemplate,但是通过为了实现loadbalance,通过我们会使用FeignClient,介绍一下如何将二者结合使用。
为了使用@EnableFeignClients
首先引入依赖管理:
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency>
启用FeignClient
@SpringBootApplication @EnableFeignClients public class ClientApplication { @Bean public RequestContextListener requestContextListener() { return new RequestContextListener(); } public static void main(String[] args) { SpringApplication.run(ClientApplication.class, args); } }
重要的配置来了:配置请求拦截器,并注入OAuth2RestTemplate
@Configuration public class OAuth2FeignAutoConfiguration { @Bean public RequestInterceptor oauth2FeignRequestInterceptor( OAuth2RestTemplate oAuth2RestTemplate) { return new OAuth2FeignRequestInterceptor(oAuth2RestTemplate); } }
实现这个拦截器:代码很简单就是把OAuth2RestTemplate 的token取出来放在restTemplate的header上,
public class OAuth2FeignRequestInterceptor implements RequestInterceptor { private static final String AUTHORIZATION_HEADER = "Authorization"; private static final String BEARER_TOKEN_TYPE = "Bearer"; private final OAuth2RestTemplate oAuth2RestTemplate; public OAuth2FeignRequestInterceptor(OAuth2RestTemplate oAuth2RestTemplate) { this.oAuth2RestTemplate = oAuth2RestTemplate; } @Override public void apply(RequestTemplate template) { System.out.println("Constructing Header "+AUTHORIZATION_HEADER+" for Token " + BEARER_TOKEN_TYPE +":" +oAuth2RestTemplate.getAccessToken().toString()); template.header(AUTHORIZATION_HEADER, String.format("%s %s", BEARER_TOKEN_TYPE, oAuth2RestTemplate.getAccessToken().toString())); } }
定义服务接口
@FeignClient(name = "resource-service", url = "http://localhost:9003/auth", fallback = ResourceServiceFallback.class ) public interface ResourceService { @RequestMapping(value = "/hello", method = RequestMethod.GET) String hello(); }
大功告成!