Spring Boot 实现第三方账号登录(OAuth协议)
2019-01-13 本文已影响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类:
- 首先添加注解
@EnableOAuth2Client
,该注解表示可以使用OAuth客户端 - 配置文件读取,且生成相应的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;
}
- 将OAuth过滤器注册到Filter过滤器中
@Bean
public FilterRegistrationBean<OAuth2ClientContextFilter> oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
FilterRegistrationBean<OAuth2ClientContextFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
- 配置进行校验的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的登录用户名