专栏首页Java工程师成长之路OAuth2简易实战(四)-Github社交联合登录

OAuth2简易实战(四)-Github社交联合登录

1. OAuth2简易实战(四)-Github社交联合登录

1.1. 用到的第三方插件

https://github.com/spring-projects/spring-social-github

1.2. 测试步骤

1.2.1. 先在github上注册一个OAuth Apps

我的配置内容如下

  1. 需要注意的,这里的最后一个回调地址的配置,格式严格规定,/connect/xxx,最后的github参数对应了特定页面,后面我通过阅读源码来详细解释
  2. 注册完之后,会有一个client id和client secret,这是需要配置到程序中的

1.2.2. 属性配置

  1. applicaton.properties
spring.social.github.app-id=xxxx
spring.social.github.app-secret=xxxx
  1. 属性类
@ConfigurationProperties(prefix = "spring.social.github")
public class GitHubProperties extends SocialProperties {

}

1.2.3. social核心配置

  1. 属性配置导入,建立与github连接
@Configuration
@EnableSocial
@EnableConfigurationProperties(GitHubProperties.class)
public class GitHubConfiguration extends SocialAutoConfigurerAdapter {

    private final GitHubProperties properties;

    public GitHubConfiguration(GitHubProperties properties) {
        this.properties = properties;
    }

    @Bean
    @Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
    public GitHub gitHub(ConnectionRepository repository) {
        Connection<GitHub> connection = repository
                .findPrimaryConnection(GitHub.class);
        return connection != null ? connection.getApi() : null;
    }

    @Bean
    public ConnectController connectController(
            ConnectionFactoryLocator factoryLocator,
            ConnectionRepository repository) {

        ConnectController controller = new ConnectController(
            factoryLocator, repository);
        controller.setApplicationUrl("http://localhost:8080");
        return controller;
    }

    @Override
    protected ConnectionFactory<?> createConnectionFactory() {
        return new GitHubConnectionFactory(properties.getAppId(),
                properties.getAppSecret());
    }
}

1.2.4. controller层

@Controller
public class RepositoriesController {

    @Autowired
    private GitHub github;

    @Autowired
    private ConnectionRepository connectionRepository;

    @GetMapping
    public String repositories(Model model) {
        if (connectionRepository.findPrimaryConnection(GitHub.class) == null) {
            return "redirect:/connect/github";
        }

        String name = github.userOperations().getUserProfile().getUsername();
        String username = github.userOperations().getUserProfile()
                .getUsername();
        model.addAttribute("name", name);

        String uri = "https://api.github.com/users/{user}/repos";
        GitHubRepo[] repos = github.restOperations().getForObject(uri,
                GitHubRepo[].class, username);
        model.addAttribute("repositories", Arrays.asList(repos));

        return "repositories";
    }

}
  1. 当我们请求localhost:8080 会重定向到localhost:8080/connect/github ,这又是写在哪呢?查看源代码,会发现在social-web包的ConnectController类中有
@Controller
@RequestMapping({"/connect"})
public class ConnectController implements InitializingBean {
    @RequestMapping(
        value = {"/{providerId}"},
        method = {RequestMethod.GET}
    )
    public String connectionStatus(@PathVariable String providerId, NativeWebRequest request, Model model) {
        this.setNoCache(request);
        this.processFlash(request, model);
        List<Connection<?>> connections = this.connectionRepository.findConnections(providerId);
        this.setNoCache(request);
        if (connections.isEmpty()) {
            return this.connectView(providerId);
        } else {
            model.addAttribute("connections", connections);
            return this.connectedView(providerId);
        }
    }
  1. 进入connectView方法
    protected String connectView(String providerId) {
        return this.getViewPath() + providerId + "Connect";
    }
  1. 可以看到,在这里它固定拼接了参数Connect,所以,在自己的跳转页面中需要有特定的命名规范,这里一定就是githubConnect.html了
<html>
<head>
    <title>Social Authcode</title>
</head>
<body>
    <h2>Connect to GitHub to see your repositories</h2>

    <form action="/connect/github" method="POST">
        <input type="hidden" name="scope" value="public_repo user" />

        <div class="formInfo">
            Click the button to share your repositories with <b>social-github</b>
        </div>
        <p><button type="submit">Connect to GitHub</button></p>
    </form>

</body>
</html>
  1. 显示页面如下
  1. 点击按钮进行post请求,进入源码如下
    @RequestMapping(
        value = {"/{providerId}"},
        method = {RequestMethod.POST}
    )
    public RedirectView connect(@PathVariable String providerId, NativeWebRequest request) {
        ConnectionFactory<?> connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);
        MultiValueMap<String, String> parameters = new LinkedMultiValueMap();
        this.preConnect(connectionFactory, parameters, request);

        try {
            return new RedirectView(this.connectSupport.buildOAuthUrl(connectionFactory, request, parameters));
        } catch (Exception var6) {
            this.sessionStrategy.setAttribute(request, "social_provider_error", var6);
            return this.connectionStatusRedirect(providerId, request);
        }
    }
  1. 层层深入后,会发现它本质还是在组装授权参数,使用的是OAuth2的授权码模式,最后组装的http请求为如下,很明显为了去获得授权码
https://github.com/login/oauth/authorize?client_id=9fc0081c3dd4f8b11f86&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fconnect%2Fgithub&scope=public_repo+user&state=e37f1891-cd45-47b4-adb4-5c541f777e60&state=48742b99-c04e-4dfd-af0a-f19b0193f1bb&state=c2737022-3cc7-4b80-92ce-fcba2ca9beb4
  1. 这最后跳转这层的代码如下,封装成buildOAuthUrl方法进行了组装
    public RedirectView connect(@PathVariable String providerId, NativeWebRequest request) {
        ConnectionFactory<?> connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);
        MultiValueMap<String, String> parameters = new LinkedMultiValueMap();
        this.preConnect(connectionFactory, parameters, request);

        try {
            return new RedirectView(this.connectSupport.buildOAuthUrl(connectionFactory, request, parameters));
        } catch (Exception var6) {
            this.sessionStrategy.setAttribute(request, "social_provider_error", var6);
            return this.connectionStatusRedirect(providerId, request);
        }
    }
  1. 获取授权码后,跳转github登录页面
  1. 输入用户名密码正确后立即回调到方法
    @RequestMapping(
        value = {"/{providerId}"},
        method = {RequestMethod.GET},
        params = {"code"}
    )
    public RedirectView oauth2Callback(@PathVariable String providerId, NativeWebRequest request) {
        try {
            OAuth2ConnectionFactory<?> connectionFactory = (OAuth2ConnectionFactory)this.connectionFactoryLocator.getConnectionFactory(providerId);
            Connection<?> connection = this.connectSupport.completeConnection(connectionFactory, request);
            this.addConnection(connection, connectionFactory, request);
        } catch (Exception var5) {
            this.sessionStrategy.setAttribute(request, "social_provider_error", var5);
            logger.warn("Exception while handling OAuth2 callback (" + var5.getMessage() + "). Redirecting to " + providerId + " connection status page.");
        }

        return this.connectionStatusRedirect(providerId, request);
    }
  1. 通过授权码再去取得token
  1. 再继续跳转/connect/github
 @RequestMapping(
        value = {"/{providerId}"},
        method = {RequestMethod.GET}
    )
    public String connectionStatus(@PathVariable String providerId, NativeWebRequest request, Model model) {
        this.setNoCache(request);
        this.processFlash(request, model);
        List<Connection<?>> connections = this.connectionRepository.findConnections(providerId);
        this.setNoCache(request);
        if (connections.isEmpty()) {
            return this.connectView(providerId);
        } else {
            model.addAttribute("connections", connections);
            return this.connectedView(providerId);
        }
    }
  1. 此时connections有值,进入connectedView方法
    protected String connectedView(String providerId) {
        return this.getViewPath() + providerId + "Connected";
    }
  1. 由此可以知道,下个页面我们命名也定下来了,githubConnected.html,这里简单一个点击连接,跳转到主页
<html>
    <head>
        <title>Social Authcode</title>
    </head>
    <body>
        <h2>Connected to GitHub</h2>

        <p>
            Click <a href="/">here</a> to see your repositories.
        </p>
    </body>
</html>
  1. 到此其实授权操作都已经完成了,接下来就是正式调用github需要权限的接口了,点击here

代码学习地址 https://github.com/spring2go/oauth2lab

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • renren-fast后端源码参考-配置和对应工具

    其中gson对象是来自qiniu-java-sdk,不需要的可以剔除或者一般国内就用fastjson

    老梁
  • bootstrap fileinput 使用记录

    多出个上传按钮,图片也会多几个按钮,我选择了删除和放大图片的按钮,还可以显示图片单独上传按钮,这里我把它去掉了,统一在下方点击上传时,全部上传。==这里重点说下...

    老梁
  • 大数据基础Hadoop 2.x入门

    老梁
  • [整理]如何做一个语法着色控件

    很多IDE或者开发工具中都有语法着色的功能,这是如何实现的呢?笔者试着用C#做了一个Sample,基本上实现此功能。

    跟着阿笨一起玩NET
  • RocketMQ存储--主从同步【源码笔记】

    1.消息存储在Master上了,如何同步到Slave上了呢? 2.同步复制和异步复制流程是怎么样的?

    瓜农老梁
  • ECMA-262-3 详解:3、This

    从来没有深入了解ECMA,网上找了一下,发现早在2010年就有大佬 Dmitry Soshnikov 总结了ECMA中的核心内容,我这里只是翻译记录,加深自己的...

    踏浪
  • 通过vue.js 学习来总结es6语法中的箭头函数,箭头函数原理分析。

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/acoolgiser/article/details/...

    acoolgiser
  • Mybatis 源码分析(一)之 Mybatis 的Executor的初始化

    Mybatis系列: Mybatis 基础介绍与逆向工程的构建 :https://www.jianshu.com/p/1c18db4d7a38 Mybati...

    zoro
  • 聊聊rocketmq的ScheduleMessageService

    rocketmq-all-4.6.0-source-release/store/src/main/java/org/apache/rocketmq/store/...

    codecraft
  • 聊聊rocketmq的ScheduleMessageService

    rocketmq-all-4.6.0-source-release/store/src/main/java/org/apache/rocketmq/store/...

    codecraft

扫码关注云+社区

领取腾讯云代金券