OAuth2.0客户端和服务端Java实现

oauth2

引言

读了《设计模式之美》和《凤凰架构》架构安全篇之后,决定写一个OAuth2.0的认证流程的Demo,也算是一个阶段性的总结,具体原理实现见《凤凰架构》( 架构安全设计篇)。

涉及到的源码可以从github.com/WeiXiao-Hyy...获取,欢迎Star!

OAuth2.0原理

主要解决的问题

面向解决第三方应用(Third-Party Application)的认证授权协议,使用Token代替用户密码作为授权的凭证。

  1. 有了令牌之后,哪怕令牌被泄漏,也不会导致密码的泄漏
  2. 令牌上可以设定访问资源的范围以及时效性
  3. 每个应用都持有独立的令牌,哪个失效都不会波及其他

OAuth2.0一共提出了四种不同的授权方式:

  • 授权码模式(Authorization Code)
  • 隐式授权模式(Implicit)
  • 密码模式(Resource Owner Password Credentials)
  • 客户端模式(Client Credentials)

本文介绍和实现的是授权码方式。

OAuth2.0流程图

  1. client请求授权服务端,获取Authorization Code;
  2. client通过Authorization Code再次请求授权服务端,获取Access Token;
  3. client通过服务端返回的Access Token获取用户的基本信息。

流程图如下所示:

第一次使用github账号应用来登陆掘金,需要进行授权

掘金即是第三方应用, 请求GitHub。

注册一个新的OAuth Application

如果我们需要使用GitHub账号来关联我们自己的第三方应用则需要完整走一遍OAuth2.0流程。

GitHub生成的client_id, client_secret(密钥), redirect_uri, homepage_url, application_name等等。

clientId如何生成唯一的

在注册clientId以及申请clientSecret时,如何保证生成的clientId是不重复的呢?

和JWT以及Cookie-Session对比

参考《凤凰架构》架构安全篇 icyfenix.cn/architect-p...

谈谈State参数为什么可以防止CSRF攻击

观察GitHub OAuth2.0实现文档,可以观察到在authroize接口需要传递一个state参数,并且在redirect_uri重定向时原封不动传递回来,所以为什么可以防止CSRF攻击呢?

核心: 在于授权服务端进行token请求绑定时,会从session将本次会话的账号与生成access_token进行绑定,而对于用户是谁并不关心。

案例

  1. 用户B登录掘金网站,并且选择绑定自己的GitHub账号;
  2. 掘金网站将用户B重定向到GitHub,由于他之前已经登录过GitHub,所以GitHub直接向他显示是否授权掘金访问的页面;
  3. 用户B在点击"同意授权"之后,截获GitHub服务器返回的含有authorization code参数的HTTP响应;
  4. 用户B精心构造一个Web页面,它会触发掘金网站向GitHub发起令牌申请的请求,而这个请求中的authorization code 参数正是上一步截获到的code;
  5. 用户B将这个Web页面放到互联网上,等待或者诱骗受害者用户A来访问;
  6. 用户A之前登录了掘金 网站,只是没有把自己的账号和其他社交账号绑定起来。在用户A访问了用户B准备的这个Web页面,令牌申请流程在用户A的浏览器里被顺利触发,掘金 网站从GitHub那里获取到access_token,但是这个token以及通过它进一步获取到的用户信息却都是攻击者用户B;
  7. 掘金网站将用户B的GitHub账号同用户A的掘金账号关联绑定起来,从此以后,用户B就可以用自己的GitHub账号通过OAuth 登录到用户A在掘金网站中的账号,堂而皇之的冒充用户A的身份执行各种操作;

代码实现(知识碎片总结)

本部分将开发过程中遇到的难点记录下来,具体源码参考此repo!

postman 请求共享session问题

本文为单体应用,使用了HttpServletRequestHttpSession方便开发,将相关的数据保存在服务器的Session中,但是Postman在发送请求的时候, 不会将其视为同一个连接,导致获取不到Session中的数据,需要每次将用户登陆的Cookie赋值到相应的接口上才能获取到Session中的数据。

实现HandlerInterceptor接口完成请求过滤

涉及到接口请求过滤条件时,可以通过实现WebMvcConfigurer接口来添加过滤规则,例如:

  1. 授权前用户没有登陆则需要重定向到登陆页面
  2. 如果access_token过期,则需要重新获取
  3. ......
java 复制代码
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    //注意注入bean的写法
    @Bean
    public OauthInterceptor oauthInterceptor() {
        return new OauthInterceptor();
    }

    @Bean
    public AuthAccessTokenInterceptor accessTokenInterceptor() {
        return new AuthAccessTokenInterceptor();
    }

    @Bean
    public LoginInterceptor loginInterceptor() {
        return new LoginInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor()).addPathPatterns("/user/**", "/oauth2.0/authorizePage", "/oauth2.0/authorize", "/sso/token");
        registry.addInterceptor(oauthInterceptor()).addPathPatterns("/oauth2.0/authorize");
        registry.addInterceptor(accessTokenInterceptor()).addPathPatterns("/api/**");
    }
}

参考资料

相关推荐
00后程序员4 分钟前
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
后端
HyggeBest9 分钟前
Mysql的数据存储结构
后端·架构
TobyMint26 分钟前
golang 实现雪花算法
后端
G探险者33 分钟前
【案例解析】一次 TIME_WAIT 导致 TPS 断崖式下降的排查与优化
后端
码农之王1 小时前
(一)TypeScript概述和环境搭建
前端·后端·typescript
玛奇玛丶1 小时前
面试官:千万级订单表新增字段怎么弄?
后端·mysql
木木一直在哭泣1 小时前
基于 XXL-JOB 的任务调度封装实践:xxl-job-start 项目详解
后端
掘金狂热勇士1 小时前
Bullet 物理引擎:开启逼真物理模拟的大门
后端
扎瓦1 小时前
ThreadLocal 线程变量
java·后端
涡能增压发动积2 小时前
一起来学 Langgraph [第一节]
后端