大家上网的时候可能会遇见这样的一个问题,就是我们去访问一个网站,但是又不想去注册这个网站的账号,账号太多了实在是记不来,于是我们可以用qq或者微信登录这个网站,简直不要太方便有没有。
这么神奇的事情怎么能不去一探究竟呢,今天我们就来给他说道说道。
其实那些第三方服务他们都是使用了Oauth2.0这个协议,做的事情都是差不多,只是细节不同而已。
什么是 OAuth 2.0?
OAuth 是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。目前,OAuth 的最新版本为 2.0
OAuth 允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth 允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要分享他们的访问许可或他们数据的所有内容
OAuth 2.0 主要有4类角色
- resource owner:资源所有者,指终端的“用户”(user)
- resource server:资源服务器,即服务提供商存放受保护资源。访问这些资源,需要获得访问令牌(access token)。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。如果,我们访问新浪博客网站,那么如果使用新浪博客的账号来登录新浪博客网站,那么新浪博客的资源和新浪博客的认证都是同一家,可以认为是同一个服务器。如果,我们是新浪博客账号去登录了知乎,那么显然知乎的资源和新浪的认证不是一个服务器。
- client:客户端,代表向受保护资源进行资源请求的第三方应用程序。
- authorization server: 授权服务器, 在验证资源所有者并获得授权成功后,将发放访问令牌给客户端
OAuth2.0的四种认证模式
OAuth 2.0定义了四种授权方式。
- 授权码模式(authorization code)
- 简化模式(implicit)(client为浏览器/前端应用)
- 密码模式(resource owner password credentials)(用户密码暴露给client端不安全)
- 客户端模式(client credentials)(主要用于api认证,跟用户无关)
这里facebook、Twitter、github、google均支持第一种模式,也是最安全的模式。
OAuth 2.0认证流程
这里主要讲的是授权码模式,见下图
上图中所涉及到的对象分别为:
- Client 第三方应用,我们的应用就是一个Client
- Resource Owner 资源所有者,即用户
- Authorization Server 授权服务器,即提供第三方登录服务的服务器,如Github
- Resource Server 拥有资源信息的服务器,通常和授权服务器属于同一应用
根据上图的信息,我们可以知道OAuth2的基本流程为:
- 用户点击客户端提供的授权请求
- 客户端请求服务的授权页面呈现给用户,用户点击确认授权后服务端返回授权许可凭证给客户端
- 客户端应用通过第二步的凭证(code)向授权服务器请求授权
- 授权服务器验证凭证(code)通过后,同意授权,并返回一个资源访问的凭证(Access Token)。
- 客户端应用通过第四步的凭证(Access Token)向资源服务器请求相关资源。
- 资源服务器验证凭证(Access Token)通过后,将客户端应用请求的资源返回。
Start doing now
github配置
好,我们了解了OAuth2.0之后,接下来介绍在spring boot2.0+security5环境下怎么实现Oauth2.0客户端第三方登录,这里我使用了github,其实qq,微信,微博那些都一样,都是基于OAuth2.0协议,只是国内这些还需要域名备案号这些信息罢了。
首先去github注册开发者应用,点击settings> Developer settings>OAuth app>new Oauth App,会看到下面的页面
- Application name 这个是你创建的应用名称,可以随便填
- Homepage URL 这个是用户使用github登陆成功后跳转的页面
- Applocation description 这个是你应用的描述,可以随便填
- Authorization callback URL 这个是你的应用向github服务端请求node的回调地址,需要配置拦截器拦截此URL获取ccode.
依赖
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
配置
#github新建app得到的id 和secret,这个需要填你自己配置的
github.client.clientId=a65cbf9654dfeb0d5142
github.client.clientSecret=5dd7327a494c4bea46b488a052fc1012dfbcc794
#获取token的地址
github.client.accessTokenUri=https://github.com/login/oauth/access_token
#登陆之后询问的地址
github.client.userAuthorizationUri=https://github.com/login/oauth/authorize
#2个Schema是指请求参数以什么样的方式跟随
github.client.authenticationScheme=query
github.client.clientAuthenticationScheme=form
#请求用户信息的url
github.client.resource.userInfoUri=https://api.github.com/user
security配置类
安全配置上需要加上@EnableWebSecurity 、 @EnableOAuth2Client注解,来启用Web security Oauth2 客户端,表明这是一个OAuth 2.0 客户端
@EnableWebSecurity
@EnableOAuth2Client // 启用 OAuth 2.0 客户端
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//注入Oauth2.0客户端上下文
@Autowired
OAuth2ClientContext oauth2ClientContext;
注册两个bean获取配置文件自定义的内容,并返回相应的类
@Bean
@ConfigurationProperties("github.client")
public AuthorizationCodeResourceDetails github() {
return new AuthorizationCodeResourceDetails();
}
@Bean
@ConfigurationProperties("github.resource")
public ResourceServerProperties githubResource() {
return new ResourceServerProperties();
}
注册一个过滤器,用来截取code的回调地址,获取token
//自定义过滤器,用于拦截oauth2第三方登录返回code的url,并根据code,clientid,clientSecret去授权服务器拿accace_token
private Filter ssoFilter() {
//OAuth2ClientAuthenticationProcessingFilter
//它的构造器需要传入defaultFilterProcessesUrl,用于指定这个filter拦截哪个url。
//它依赖OAuth2RestTemplate来获取token
//还依赖ResourceServerTokenServices进行校验token
OAuth2ClientAuthenticationProcessingFilter githubFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/github");
//对rest template的封装,为获取token等提供便捷方法
//DefaultUserInfoRestTemplateFactory实例了OAuth2RestTemplate,这个提供了OAuth2RestTemplate
OAuth2RestTemplate githubTemplate = new OAuth2RestTemplate(github(), oauth2ClientContext);
githubFilter.setRestTemplate(githubTemplate);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(githubResource().getUserInfoUri(), github().getClientId());
tokenServices.setRestTemplate(githubTemplate);
githubFilter.setTokenServices(tokenServices);
return githubFilter;
}
注意这里的/login/github对应与在github填的Authorization callback URL。页面上使用github登录的链接也要写这个,security会帮我们做好一切流程
接下来需要注册我们的过滤器
/*注册一个额外的Filter:OAuth2ClientContextFilter
* 主要作用是重定向,当遇到需要权限的页面或URL,代码抛出异常,这时这个Filter将重定向到OAuth鉴权的地址
*/
@Bean
public FilterRegistrationBean<OAuth2ClientContextFilter> oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
FilterRegistrationBean<OAuth2ClientContextFilter> registration = new FilterRegistrationBean<OAuth2ClientContextFilter>();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
最后还有至关重要的一步,把自定义的过滤器添加到过滤器链中基本过滤器的前面
//在网站基本认证之前添加ssoFilter过滤器
http.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
如此这般,所有的工作就完成了
打开项目,点击页面上使用github登录的链接
点击授权之后
可以看到github的服务器给我们颁发了code,然后将页面重定向到我们配置的Homepage URL ,至此整个Oauth2.0的认证流程结束。
这里只要配置客户端,@EnableOAuth2Client就可以支持所有的社交平台,关键有一点,如果用户时第一次登陆,需要将用户信息注册到我们自己的数据库中,与数据库中的用户一一映射,就可以判断当前登陆的用户是对应我们数据库自己的平台身份。并且,Oauth2登陆时,我们将自定义的用户身份(也就是社交平台对应自己数据库的User)返回给security,进行登陆。这样就进行了用户绑定。这里主要实现了自定义的PrincipalExtractor接口,并非使用默认实现FixedPrincipalExtractor。参考这篇文章
源码
这里就不贴我的代码了,放一下官网的demo,其实也一样