12-SpringSecurity:通过OAuth2集成Github登录

简介: 12-SpringSecurity:通过OAuth2集成Github登录

背景


本系列教程,是作为团队内部的培训资料准备的。主要以实验的方式来体验 SpringSecurity 的各项Feature。


目前 SpringSecurity 新版本已实现了对 OAuth2.0 的支持。 OAuth2.0 是一个开放的授权标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户凭据(用户名、密码)提供给第三方网站。


SpringSecurity 本身提供了 GOOGLEGITHUBFACEBOOKOKTAOAuth2.0 接入支持,具体源码在枚举类 CommonOAuth2Provider 中。这里仅对 SpringSecurity 中的 OAuth2.0 这一新特性做个体验:将我们的应用(第三方网站)作为一个 OAuth2.0 的客户端来集成 Github 登录( OAuth2.0 服务端),并实现对Github资源(资源服务器)的访问。


实验0:OAuth2.0客户端的Github登录


(1) 注册应用


在Github注册一个应用,生成 client-idclient-secret

image.png

注意这里的Home页: http://localhost:8080 ,以及回调地址: http://localhost:8080/login/oauth2/code/github


(2) 配置 application.yml


spring:
  security:
    oauth2:
      client:
        registration:
          github:
            client-id: <your-client-id>
            client-secret: <your-client-secret>

(3) 启动应用


为了看到登录成功后的效果,这里增加一个 Controller ;然后运行应用。

@GetMapping(value = "/")
    public String index() {
        log.info(SecurityContextHolder.getContext().getAuthentication().toString());
        return "Welcome " + SecurityContextHolder.getContext().getAuthentication();
    }

在浏览器键入: http://localhost:8080/login ,返回一个页面,其中包含了 Github 登录链接

image.png

点击 Github 登录链接,会自动跳转至 Github 的认证页:

image.png

输入Github的账号、密码,会进入我们之前配置的Home页:


image.png

可通过链接退出: http://localhost:8080/logout

借助 SpringSecurityOAuth2.0 的支持,我们几乎不用写什么代码就实现了 Github 登录集成。下面简单了解下登录成功后的 RegistrationAccessToken


实验1:查看Github在我们应用中的注册信息


为了方便调试或查看 registration ,这里新增一个接口端点:

@GetMapping(value = "/user/reg")
    public String registration() {
        ClientRegistration githubRegistration = this.clientRegistrationRepository.findByRegistrationId("github");
        log.info(githubRegistration.toString());
        return githubRegistration.toString();
    }

访问之后会返回 registration 信息,其中包含了 clientIdclientSecretauthorizationGrantTyperedirectUriscopes 等。

image.png


实验2:查看获取到的AccessToken


@GetMapping(value = "/user/token")
    public OAuth2AccessToken accessToken(OAuth2AuthenticationToken authentication) {
        OAuth2AuthorizedClient authorizedClient = this.authorizedClientService.loadAuthorizedClient(
                authentication.getAuthorizedClientRegistrationId(), authentication.getName());
        OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
        return accessToken;
    }

image.png

Note: 这里的 issuedAtexpiresAt 着实诡异,仅差了一秒,是Github授权服务问题?还没细看是什么原因。。


实验3:通过AccessToken请求Github的API


定义抽象 API 绑定类,通过拦截器将获取到的 AccessToken 设置到后续请求头中,通过 RestTemplate 实现对 API 的请求:

public abstract class ApiBinding {
    protected RestTemplate restTemplate;
    public ApiBinding(String accessToken) {
        this.restTemplate = new RestTemplate();
        if (accessToken != null) {
            this.restTemplate.getInterceptors().add(getBearerTokenInterceptor(accessToken));
        } else {
            this.restTemplate.getInterceptors().add(getNoTokenInterceptor());
        }
    }
    private ClientHttpRequestInterceptor getBearerTokenInterceptor(String accessToken) {
        return new ClientHttpRequestInterceptor() {
            @Override
            public ClientHttpResponse intercept(HttpRequest request, byte[] bytes, ClientHttpRequestExecution execution) throws IOException {
                request.getHeaders().add("Authorization", "Bearer " + accessToken);
                return execution.execute(request, bytes);
            }
        };
    }
    private ClientHttpRequestInterceptor getNoTokenInterceptor() {
        return new ClientHttpRequestInterceptor() {
            @Override
            public ClientHttpResponse intercept(HttpRequest request, byte[] bytes, ClientHttpRequestExecution execution) throws IOException {
                throw new IllegalStateException("Can't access the Github API without an access token");
            }
        };
    }
}

将获取 AccessToken 的过程进行封装:

@Configuration
@Slf4j
public class SocialConfig {
    @Bean
    @RequestScope
    public Github github(OAuth2AuthorizedClientService clientService) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String accessToken = null;
        if (authentication.getClass().isAssignableFrom(OAuth2AuthenticationToken.class)) {
            OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication;
            String clientRegistrationId = oauthToken.getAuthorizedClientRegistrationId();
            if (clientRegistrationId.equals("github")) {
                OAuth2AuthorizedClient client = clientService.loadAuthorizedClient(clientRegistrationId, oauthToken.getName());
                if (client != null) {
                    accessToken = client.getAccessToken().getTokenValue();
                }
                log.info(accessToken);
            }
        }
        return new Github(accessToken);
    }
}
public class Github extends ApiBinding {
    private static final String BASE_URL = "https://api.github.com";
    public Github(String accessToken) {
        super(accessToken);
    }
    public String getProfile() {
        return restTemplate.getForObject(BASE_URL + "/user", String.class);
    }
}

Controller 中新增接口:通过 AccessToken 获取 Github 用户信息:

@GetMapping(value = "/user/info")
    public String info() {
        String profile = github.getProfile();
        log.info(github.getProfile());
        return profile;
    }

image.png

Note:两个接口的区别:api.github.com/users/heart…

image.png

Controller 的完整代码:

@RestController
@Slf4j
public class HelloController {
    @Autowired
    private ClientRegistrationRepository clientRegistrationRepository;
    @Autowired
    private OAuth2AuthorizedClientService authorizedClientService;
    @Autowired
    Github github;
    @GetMapping(value = "/")
    public String index() {
        log.info(SecurityContextHolder.getContext().getAuthentication().toString());
        return "Welcome " + SecurityContextHolder.getContext().getAuthentication();
    }
    @GetMapping(value = "/user/reg")
    public String registration() {
        ClientRegistration githubRegistration = this.clientRegistrationRepository.findByRegistrationId("github");
        log.info(githubRegistration.toString());
        return githubRegistration.toString();
    }
    @GetMapping(value = "/user/token")
    public OAuth2AccessToken accessToken(OAuth2AuthenticationToken authentication) {
        OAuth2AuthorizedClient authorizedClient = this.authorizedClientService.loadAuthorizedClient(
                authentication.getAuthorizedClientRegistrationId(), authentication.getName());
        OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
        return accessToken;
    }
    @GetMapping(value = "/user/info")
    public String info() {
        String profile = github.getProfile();
        log.info(github.getProfile());
        return profile;
    }
}



目录
相关文章
|
5月前
GitHub和Gitee的基本使用和在IDEA中的集成
GitHub和Gitee的基本使用和在IDEA中的集成
64 0
|
21天前
|
Shell 网络安全 开发工具
Git,GitHub,Gitee&IDEA集成Git
Git提交项目到GitHub简洁版、版本控制、安装、常用命令、分支、团队协作机制、Github、Gitee远程仓库、IDEA集成Git、IDEA集成Github、IDEA集成Gitee
Git,GitHub,Gitee&IDEA集成Git
|
2月前
|
NoSQL 关系型数据库 MySQL
SpringBoot 集成 SpringSecurity + MySQL + JWT 附源码,废话不多直接盘
SpringBoot 集成 SpringSecurity + MySQL + JWT 附源码,废话不多直接盘
93 2
|
2月前
|
jenkins 持续交付
jenkins学习笔记之九:jenkins认证集成github
jenkins学习笔记之九:jenkins认证集成github
|
2月前
|
JavaScript Linux API
【Azure 应用服务】NodeJS Express + MSAL 应用实现AAD集成登录并部署在App Service Linux环境中的实现步骤
【Azure 应用服务】NodeJS Express + MSAL 应用实现AAD集成登录并部署在App Service Linux环境中的实现步骤
|
4月前
|
存储 NoSQL Java
大事件后端项目34_登录优化----redis_SpringBoot集成redis
大事件后端项目34_登录优化----redis_SpringBoot集成redis
大事件后端项目34_登录优化----redis_SpringBoot集成redis
|
4月前
|
存储 安全 Java
Spring Security与OAuth2集成开发
Spring Security与OAuth2集成开发
|
3月前
|
存储 安全 Java
在Spring Boot中集成OAuth2
在Spring Boot中集成OAuth2
|
3月前
|
存储 安全 Java
在Spring Boot中集成OAuth2实现安全认证
在Spring Boot中集成OAuth2实现安全认证
|
4月前
|
Java 数据库
Springboot集成SpringSecurity有源码
Springboot集成SpringSecurity有源码
下一篇
无影云桌面