基于@EnableOAuth2Sso实现

前面我们将验证服务器已经搭建完成了,现在我们就来实现一下单点登陆吧,SpringCloud为我们提供了客户端的直接实现,我们只需要添加一个注解和少量配置即可将我们的服务作为一个单点登陆应用,使用的是第四种授权码模式。

一句话来说就是,这种模式只是将验证方式由原本的默认登录形式改变为了统一在授权服务器登陆的形式。

首先还是依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
    <version>2.2.5.RELEASE</version>
</dependency>

我们只需要直接在启动类上添加即可:

@EnableOAuth2Sso
@SpringBootApplication
public class BookApplication {
    public static void main(String[] args) {
        SpringApplication.run(BookApplication.class, args);
    }
}

我们不需要进行额外的配置类,因为这个注解已经帮我们做了:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EnableOAuth2Client
@EnableConfigurationProperties({OAuth2SsoProperties.class})
@Import({OAuth2SsoDefaultConfiguration.class, OAuth2SsoCustomConfiguration.class, ResourceServerTokenServicesConfiguration.class})
public @interface EnableOAuth2Sso {
}

可以看到它直接注册了OAuth2SsoDefaultConfiguration,而这个类就是帮助我们对Security进行配置的:

@Configuration
@Conditional({NeedsWebSecurityCondition.class})
public class OAuth2SsoDefaultConfiguration extends WebSecurityConfigurerAdapter {
  	//直接继承的WebSecurityConfigurerAdapter,帮我们把验证设置都写好了
    private final ApplicationContext applicationContext;

    public OAuth2SsoDefaultConfiguration(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

接着我们需要在配置文件中配置我们的验证服务器相关信息:

security:
  oauth2:
    client:
      #不多说了
      client-id: web
      client-secret: 654321
      #Token获取地址
      access-token-uri: http://localhost:8500/sso/oauth/token
      #验证页面地址
      user-authorization-uri: http://localhost:8500/sso/oauth/authorize
    resource:
      #Token信息获取和校验地址
      token-info-uri: http://localhost:8500/sso/oauth/check_token

现在我们就开启图书服务,调用图书接口:

spring cloud实现单点登录 springcloud整合oauth2单点登录_SpringCloud

可以看到在发现没有登录验证时,会直接跳转到授权页面,进行授权登录,之后才可以继续访问图书服务:

那么用户信息呢?是否也一并保存过来了?我们这里直接获取一下SpringSecurity的Context查看用户信息,获取方式跟我们之前的视频中讲解的是一样的:

@RequestMapping("/book/{bid}")
Book findBookById(@PathVariable("bid") int bid){
  	//通过SecurityContextHolder将用户信息取出
    SecurityContext context = SecurityContextHolder.getContext();
    System.out.println(context.getAuthentication());
    return service.getBookById(bid);
}

再次访问图书管理接口,可以看到:

这里使用的不是之前的UsernamePasswordAuthenticationToken也不是RememberMeAuthenticationToken,而是新的OAuth2Authentication,它保存了验证服务器的一些信息,以及经过我们之前的登陆流程之后,验证服务器发放给客户端的Token信息,并通过Token信息在验证服务器进行验证获取用户信息,最后保存到Session中,表示用户已验证,所以本质上还是要依赖浏览器存Cookie的。

接下来我们将所有的服务都使用这种方式进行验证,别忘了把重定向地址给所有服务都加上:

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients
            .inMemory()
            .withClient("web")
            .secret(encoder.encode("654321"))
            .autoApprove(true)   //这里把自动审批开了,就不用再去手动选同意了
            .scopes("book", "user", "borrow")
            .redirectUris("http://localhost:8101/login", "http://localhost:8201/login", "http://localhost:8301/login")
            .authorizedGrantTypes("client_credentials", "password", "implicit", "authorization_code", "refresh_token");
}

这样我们就可以实现只在验证服务器登陆,如果登陆过其他的服务都可以访问了。

但是我们发现一个问题,就是由于SESSION不同步,每次切换不同的服务进行访问都会重新导验证服务器去验证一次:

spring cloud实现单点登录 springcloud整合oauth2单点登录_SpringCloud_02

这里有两个方案:

  • 像之前一样做SESSION统一存储
  • 设置context-path路径,每个服务单独设置,就不会打架了

但是这样依然没法解决服务间调用的问题,所以仅仅依靠单点登陆的模式不太行。