美文网首页
Spring Boot 实现第三方账号登录(OAuth协议)

Spring Boot 实现第三方账号登录(OAuth协议)

作者: 秋风落叶黄 | 来源:发表于2019-01-13 17:07 被阅读0次

        最近项目涉及到了第三方登录,因此了解下其实现,发现目前大部分第三方登录都是采用Oauth协议实现的,因此参考了SpringBoot的文档实现了一个简单的demo,使用SpringBoot提供的客户端实现github登录认证。

    项目总体结构如下:其中重点为红色框内内容


    image.png

    1. 创建一个SpringBoot项目(基于Maven依赖管理)

        项目具体如何创建请参考其他入门教程,该项目是基于SpringBoot2.1.1版本实现的,下面是完整的Maven依赖:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.1.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.example</groupId>
        <artifactId>oauth</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>war</packaging>
        <name>oauth</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.webjars</groupId>
                <artifactId>jquery</artifactId>
                <version>2.1.1</version>
            </dependency>
            <dependency>
                <groupId>org.webjars</groupId>
                <artifactId>bootstrap</artifactId>
                <version>3.2.0</version>
            </dependency>
            <dependency>
                <groupId>org.webjars</groupId>
                <artifactId>webjars-locator-core</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.security.oauth.boot</groupId>
                <artifactId>spring-security-oauth2-autoconfigure</artifactId>
                <version>2.0.0.RELEASE</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.webjars/js-cookie -->
            <dependency>
                <groupId>org.webjars</groupId>
                <artifactId>js-cookie</artifactId>
                <version>2.1.0</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
    

    2. 创建一个前端界面

        在/resource/static下面创建一个index.html,用户触发跳转github登录验证地址,具体代码如下:

    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="utf-8"/>
        <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
        <title>Demo</title>
        <meta name="description" content=""/>
        <meta name="viewport" content="width=device-width"/>
        <base href="/"/>
        <link rel="stylesheet" type="text/css" href="/webjars/bootstrap/css/bootstrap.min.css"/>
        <script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script>
        <script type="text/javascript" src="/webjars/bootstrap/js/bootstrap.min.js"></script>
        <script type="text/javascript" src="/webjars/js-cookie/js.cookie.js"></script>
    </head>
    <body>
    <h1>Demo</h1>
    
    <div class="container unauthenticated">
        With GitHub: <a href="/login/github">click here</a>
    </div>
    <div class="container authenticated" style="display:none">
        Logged in as: <span id="user"></span>
        <div>
            <button onClick="logout()" class="btn btn-primary">Logout</button>
        </div>
    </div>
    
    </body>
    <script type="text/javascript">
        $.get("/user", function(data) {
            $("#user").html(data.userAuthentication.name);
            $(".unauthenticated").hide()
            $(".authenticated").show()
        });
    
        var logout = function() {
            $.post("/logout", function() {
                $("#user").html('');
                $(".unauthenticated").show();
                $(".authenticated").hide();
            })
            return true;
        }
    
        $.ajaxSetup({
            beforeSend : function(xhr, settings) {
                if (settings.type == 'POST' || settings.type == 'PUT'
                    || settings.type == 'DELETE') {
                    if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
                        // Only send the token to relative URLs i.e. locally.
                        xhr.setRequestHeader("X-XSRF-TOKEN",
                            Cookies.get('XSRF-TOKEN'));
                    }
                }
            }
        });
    </script>
    </html>
    

    如下脚本主要用于获取oauth认证后的数据,进行显示。

       $.get("/user", function(data) {
            $("#user").html(data.userAuthentication.name);
            $(".unauthenticated").hide()
            $(".authenticated").show()
        });
    

    下面这个脚本主要实现注销功能:

        var logout = function() {
            $.post("/logout", function() {
                $("#user").html('');
                $(".unauthenticated").show();
                $(".authenticated").hide();
            })
            return true;
        }
    

    下面这个脚本主要是防止跨域请求攻击:

    $.ajaxSetup({
            beforeSend : function(xhr, settings) {
                if (settings.type == 'POST' || settings.type == 'PUT'
                    || settings.type == 'DELETE') {
                    if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
                        // Only send the token to relative URLs i.e. locally.
                        xhr.setRequestHeader("X-XSRF-TOKEN",
                            Cookies.get('XSRF-TOKEN'));
                    }
                }
            }
        });
    

    3.添加配置文件

    在application.yml文件下添加如下配置:

    github:
      client:
        clientId: bd1c0a783ccdd1c9b9e4
        clientSecret: 1a9030fbca47a5b2c28e92f19050bb77824b5ad1
        accessTokenUri: https://github.com/login/oauth/access_token
        userAuthorizationUri: https://github.com/login/oauth/authorize
        clientAuthenticationScheme: form
      resource:
        userInfoUri: https://api.github.com/user
    
    spring:
      main:
        allow-bean-definition-overriding: true
    

    配置文件主要是github进行OAuth认证的一些配置,而allow-bean-definition-overriding: true主要是解决SpringBoot自带框架和Spring Security框架里面的包冲突。

    4. 具体实现

    下面先给出代码,讲解放在后面,现在你已经可以实现github登录自己的项目了。

    ServletInitializer类:

    package com.example.oauth;
    
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
    
    public class ServletInitializer extends SpringBootServletInitializer {
    
        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
            return application.sources(OauthApplication.class);
        }
    }
    

    OauthApplication类:

    package com.example.oauth;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.oauth2.client.OAuth2ClientContext;
    import org.springframework.security.oauth2.client.OAuth2RestTemplate;
    import org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter;
    import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter;
    import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;
    import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client;
    import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
    import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.security.Principal;
    import javax.servlet.Filter;
    
    @SpringBootApplication
    @EnableOAuth2Client
    @RestController
    /**
     * Oauth客户端调用Github测试
     *
     * @author huangyichun
     */
    public class OauthApplication extends WebSecurityConfigurerAdapter {
    
        @Autowired
        OAuth2ClientContext auth2ClientContext;
    
        @RequestMapping("/user")
        public Principal user(Principal principal) {
            return principal;
        }
    
        public static void main(String[] args) {
            SpringApplication.run(OauthApplication.class, args);
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.antMatcher("/**")
                    .authorizeRequests().antMatchers("/", "/login**", "/webjars/**", "/error**").permitAll()
                    .anyRequest().authenticated()
                    .and().logout().logoutSuccessUrl("/").permitAll()
                    .and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                    .and().addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
        }
    
        private Filter ssoFilter() {
            OAuth2ClientAuthenticationProcessingFilter githubFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/github");
            OAuth2RestTemplate githubTemplate = new OAuth2RestTemplate(github(), auth2ClientContext);
            githubFilter.setRestTemplate(githubTemplate);
            UserInfoTokenServices tokenServices = new UserInfoTokenServices(githubResource().getUserInfoUri(), github().getClientId());
            tokenServices.setRestTemplate(githubTemplate);
            githubFilter.setTokenServices(tokenServices);
            return githubFilter;
        }
    
        @Bean
        @ConfigurationProperties("github.client")
        public AuthorizationCodeResourceDetails github() {
            return new AuthorizationCodeResourceDetails();
        }
    
        @Bean
        @ConfigurationProperties("github.resource")
        public ResourceServerProperties githubResource() {
            return new ResourceServerProperties();
        }
    
        @Bean
        public FilterRegistrationBean<OAuth2ClientContextFilter> oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
            FilterRegistrationBean<OAuth2ClientContextFilter> registration = new FilterRegistrationBean<>();
            registration.setFilter(filter);
            registration.setOrder(-100);
            return registration;
        }
    }
    
    

    其中重点解释下OauthApplication类:

    1. 首先添加注解@EnableOAuth2Client,该注解表示可以使用OAuth客户端
    2. 配置文件读取,且生成相应的bean
        @Bean
        @ConfigurationProperties("github.client")
        public AuthorizationCodeResourceDetails github() {
            return new AuthorizationCodeResourceDetails();
        }
    
        @Bean
        @ConfigurationProperties("github.resource")
        public ResourceServerProperties githubResource() {
            return new ResourceServerProperties();
        }
    

    3.添加Github身份验证过滤器

    private Filter ssoFilter() {
            OAuth2ClientAuthenticationProcessingFilter githubFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/github");
            OAuth2RestTemplate githubTemplate = new OAuth2RestTemplate(github(), auth2ClientContext);
            githubFilter.setRestTemplate(githubTemplate);
            UserInfoTokenServices tokenServices = new UserInfoTokenServices(githubResource().getUserInfoUri(), github().getClientId());
            tokenServices.setRestTemplate(githubTemplate);
            githubFilter.setTokenServices(tokenServices);
            return githubFilter;
        }
    
    1. 将OAuth过滤器注册到Filter过滤器中
        @Bean
        public FilterRegistrationBean<OAuth2ClientContextFilter> oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
            FilterRegistrationBean<OAuth2ClientContextFilter> registration = new FilterRegistrationBean<>();
            registration.setFilter(filter);
            registration.setOrder(-100);
            return registration;
        }
    
    1. 配置进行校验的Url
     @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.antMatcher("/**")
                    .authorizeRequests().antMatchers("/", "/login**", "/webjars/**", "/error**").permitAll()
                    .anyRequest().authenticated()
                    .and().logout().logoutSuccessUrl("/").permitAll()
                    .and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                    .and().addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
        }
    

    5. 运行结果

    • 请求: localhost:8080地址,进入首页,然后点击click_here,跳转到github登录地址:


      image.png
    • 输入账号密码后,跳转回初始页面,且能正确展示github的登录用户名


    相关文章

      网友评论

          本文标题:Spring Boot 实现第三方账号登录(OAuth协议)

          本文链接:https://www.haomeiwen.com/subject/ouzkdqtx.html