Dubbo 集成 Spring Security 和 OAuth2

定义与基本概念 Spring Security 是一个基于 Spring 框架的安全框架,为 Java 企业级应用提供了全面的安全解决方案,涵盖了身份验证(用户登录)、授权(访问控制)、加密和防止常见的安全漏洞等功能。它可以集成到各种类型的 Spring 应用中,包括 Web 应用(如 Spring MVC、Spring Boot)和非 Web 应用。 OAuth2(开放授权 2.0)是一个开放标准的授权框架,旨在解决不同应用之间的授权问题,允许用户在不向第三方应用透露自己的用户名和密码的情况下,授权第三方应用访问他们存储在另一个服务提供商上的资源。它通过使用令牌Token来代表用户的授权,使得资源服务器能够验证请求是否被授权。 核心角色与流程 资源所有者:通常是用户,拥有受保护的资源。 资源服务器:存储资源的服务器,需要对请求进行授权验证。 客户端:请求访问资源的应用程序。 授权服务器:负责验证资源所有者的身份,并发放令牌给客户端 客户端请求资源所有者授权,资源所有者同意后,授权服务器向客户端发放令牌,客户端携带令牌访问资源服务器,资源服务器验证令牌的有效性后提供资源。 与 Dubbo 的协同 同时低版本 javax 和高版本 jakarta servlet API ,jakarta API 优先级更高,只需要引入jar即可使用HttpServletRequest和HttpServletResponse作为参数 使用 Filter 扩展: 实现 Filter 接口和 org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension 接口,然后注册SPI 主要流程 由于 OAuth2 需要授权服务器,资源服务器,客户端,为了简化案例,就写授权服务器和资源服务器。 AuthorizationServer 采用默认配置 1 2 3 4 5 6 @Bean public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); return http.build(); } 1. 端点访问控制 授权端点(/authorize): 要求进行身份验证,只有已认证的用户才能发起授权请求。默认情况下,通常会使用 Spring Security 的标准身份验证机制,如基于表单的登录或 HTTP Basic 认证。 限制对该端点的请求方法,一般只允许 GET 和 POST 方法,以防止恶意的请求操作。 令牌端点(/token): 此端点用于发放访问令牌和刷新令牌,安全要求更高。默认配置会要求客户端进行身份验证,通常通过客户端 ID 和客户端密钥进行认证。 同样限制请求方法,一般只允许 POST 方法,以确保只有合法的请求才能获取令牌。 2. 防止跨站请求伪造(CSRF) 启用 CSRF 保护机制,CSRF 是一种常见的网络攻击手段,攻击者通过在用户已登录的情况下,利用用户的浏览器自动发送恶意请求。默认配置会在授权服务器的相关请求中添加 CSRF 防护措施,例如在表单提交时要求包含 CSRF 令牌。 对于一些与 OAuth2 流程紧密相关的请求,可能会根据具体情况对 CSRF 保护进行特殊配置,例如在某些情况下允许特定的请求绕过 CSRF 检查,但这需要谨慎处理以确保安全性。 3. 安全头信息设置 添加各种安全相关的 HTTP 头信息,以增强安全性。例如: Content-Security-Policy:用于限制网页可以加载的资源来源,防止跨站脚本攻击(XSS)。 X-Frame-Options:防止页面被嵌入到其他页面的框架中,避免点击劫持攻击。 X-XSS-Protection:启用浏览器的 XSS 过滤机制,帮助检测和阻止 XSS 攻击。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private static final String HOST = System.getProperty("authorization.address", "localhost"); String issuer = "http://" + HOST + ":9000"; @Bean public RegisteredClientRepository registeredClientRepository() { RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString()) .clientId("49fd8518-12eb-422b-9264-2bae0ab89f66") //configure the client id .clientSecret("{noop}H3DTtm2fR3GRAdr4ls1mcg") // configure the client secret .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) .redirectUri("http://localhost:9000/oauth2/token") // configure the redirect uri .scope("openid") .scope("read") .scope("write") .build(); return new InMemoryRegisteredClientRepository(registeredClient); } @Bean public AuthorizationServerSettings authorizationServerSettings() { return AuthorizationServerSettings.builder() .issuer(issuer) // set the address of the authorization server .build(); } 配置相关授权信息,并进行相关接口的暴露 ...

December 26, 2024 · 5 min · 860 words · Similarityoung

一些较好的文章收藏

go 的代码风格 git commit 规范 git 教程 The Uber Go Style Guide

December 1, 2024 · 1 min · 12 words · Similarityoung

Zinx 框架学习笔记

函数返回接口而不是具体的结构体 Q: 为什么返回接口 IServer 而不是 server 1 2 3 4 5 6 /* 创建一个服务器句柄 */ func NewServer (name string) ziface.IServer { s:= &Server { Name :name, IPVersion:"tcp4", IP:"0.0.0.0", Port:7777, } return s } A : NewServer 函数返回一个 ziface.IServer 类型的接口而不是直接返回 Server 类型的结构体,这种设计方式具有几个优点: 抽象化:返回一个接口而不是具体的结构体可以抽象化组件之间的交互。使用接口可以隐藏具体实现的细节,使得调用者只需要关心接口提供的方法。这样,如果未来需要改变具体的实现,只要新的实现符合接口的定义,调用代码不需要做任何修改。 灵活性和扩展性:使用接口使得代码更加灵活和易于扩展。比如,如果你想实现一个具有不同特性的服务器,只需创建一个新的结构体,实现相同的接口。这样你可以在不修改现有代码的情况下,为系统引入新的功能。 依赖倒置原则:这是面向对象设计原则之一,主张高层模块不应该依赖低层模块,它们都应该依赖于抽象。接口作为一种高级的抽象,确保了系统各个部分的低耦合性,增加了代码的稳定性。 测试和模拟:使用接口可以更容易地进行单元测试。你可以创建一个或多个模拟(mock)对象来实现同一接口,这样就可以在不涉及具体服务器实现的情况下测试系统的其他部分。 因此,返回 ziface.IServer 接口而不是具体的 Server 结构体,在软件工程中是一种常见且有益的做法,特别是在构建需要高度维护和扩展性的大型软件系统时。这种方式提供了更好的解耦和更灵活的代码管理策略。 函数定义的使用 函数类型的定义在编程中非常有用,尤其在Go语言这样的类型安全语言中,它提供了一系列的好处,这些好处涵盖了代码的可维护性、灵活性和可重用性等方面: 代码复用: 通过定义一种函数签名,可以创建多个遵循相同模式的函数,这有助于统一接口和减少重复代码。这意味着你可以编写高度模块化的代码,其中特定的功能可以由符合同一签名的不同函数实现。 实现抽象和封装: 函数类型的定义允许程序员封装复杂的逻辑,并通过简单的接口暴露功能,使得其他部分的代码不必了解背后的细节就能使用该功能。这有助于降低程序各部分之间的耦合度。 提高灵活性: 函数类型作为参数传递或作为返回类型使用时,可以使代码更加灵活。开发者可以根据具体的需要传入不同的函数实现,例如在处理不同类型的网络请求时,可以根据请求的类型动态选择合适的处理函数。 便于维护和扩展: 函数类型定义使得未来的修改和扩展变得更容易。如果需要修改功能,只需替换或修改实现了该函数类型的具体函数,而不需修改依赖于该类型的代码。这样做减少了对现有代码的干扰,降低了引入新错误的风险。 支持回调和高阶函数: 函数类型的定义是实现回调机制的基础。在Go中,经常会用到回调来处理异步事件、定时任务或在框架中允许用户代码介入框架运行。同时,函数类型的定义是实现高阶函数(接受函数为参数或返回函数的函数)的关键,这在功能编程风格中非常有用。 类型安全: 函数类型提供了类型安全的好处,确保函数的使用者传递正确类型的参数,返回预期类型的数据。这在编译时就能捕捉到许多可能的错误,提高程序的健壮性。 panic 具体使用方法 以下是一个使用 ProcessFunc 类型函数作为参数的示例,该函数将遍历一个字符串切片,并使用传入的 ProcessFunc 类型函数处理每个字符串,然后将结果收集并返回。 首先,我们有一个 ProcessFunc 类型定义,如之前所述: 1 type ProcessFunc func(input string) int 现在,我们将创建一个函数 applyToStrings,它接受一个 ProcessFunc 类型的函数和一个字符串切片,应用该函数到每个字符串,并收集结果: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package main import ( "fmt" ) // 定义ProcessFunc类型 type ProcessFunc func(input string) int // 实现一个计算字符串长度的ProcessFunc func countCharacters(s string) int { return len(s) } // 实现一个返回固定值的ProcessFunc func fixedValue(s string) int { return 42 } // applyToStrings 接受一个ProcessFunc和字符串切片,应用函数到每个字符串 func applyToStrings(f ProcessFunc, inputs []string) []int { results := make([]int, len(inputs)) for i, s := range inputs { results[i] = f(s) // 应用ProcessFunc到每个字符串 } return results } func main() { strings := []string{"hello", "world", "go", "programming"} // 使用countCharacters函数 lengths := applyToStrings(countCharacters, strings) fmt.Println("字符串长度:", lengths) // 使用fixedValue函数 values := applyToStrings(fixedValue, strings) fmt.Println("固定值:", values) } 在这个例子中: ...

September 24, 2024 · 2 min · 285 words · Similarityoung

Markdown 编写规范

说明 文档中使用的关键字「MUST」,「MUST NOT」,「REQUIRED」,「SHALL」,「SHALL NOT」,「SHOULD」,「SHOULD NOT」,「RECOMMENDED」,「MAY」和「OPTIONAL」在 RFC2119 中有说明。 还未定稿,对规范中提及的点有不赞同的欢迎提出 issues(请添加 markdown 标签)讨论。 规则 后缀必须「MUST」使用 .md。 文件名必须「MUST」使用小写,多个单词之间使用-分隔。 文件编码必须「MUST」用 UTF-8。 文档标题应该「SHOULD」这样写。 1 2 Markdown 编写规范 ========================== 章节标题必须「MUST」以 ## 开始,而不是 #。 章节标题必须「MUST」在 # 后加一个空格,且后面没有 #。 1 2 3 4 5 6 7 8 // bad ##章节1 // bad ## 章节1 ## // good ## 章节1 章节标题和内容间必须「MUST」有一个空行。 1 2 3 4 5 6 7 8 9 10 11 // bad ## 章节1 内容 ## 章节2 // good ## 章节1 内容 ## 章节2 代码段的必须「MUST」使用 Fenced code blocks 风格,如下所示: ...

September 24, 2024 · 2 min · 254 words · Similarityoung

Git 约定式提交(Conventional Commits)

文档连接 语义化版本: 用于发版相关的规范 约定式提交(Conventional Commits)是一种用于写作提交消息的规范,它规定了一套标准化的提交消息格式,以使得项目的版本控制更加清晰和一致。采用这种规范的好处是能够帮助开发团队更好地理解代码的变更历史、生成变更日志(changelog),以及进行版本控制。 约定式提交的基本格式 1 2 3 4 5 <类型>(<范围>): <描述> <主体> <footer> 各部分说明 1. 类型(type):用于说明提交的类型,例如是修复bug还是添加新功能。常见的类型包括: feat:新功能(feature) fix:修补bug docs:文档(documentation)变更 style:代码格式(不影响代码运行的变动) - refactor:重构(即不是新增功能,也不是修改bug的代码变动) - test:增加测试 - chore:构建过程或辅助工具的变动 2. 范围(scope):可选项,用于说明提交影响的范围(例如模块、文件等)。 3. 描述(subject):简要说明提交的内容。 4. 主体(body):可选项,用于详细说明提交的内容,可以分成多行。 5. 脚注(footer):可选项,用于说明重大变更,或者关联的issue。例如: - BREAKING CHANGE:说明重大变更 - Closes #123:关闭issue 示例 1 2 3 4 5 feat(auth): add login functionality Added login functionality with OAuth2 integration. Users can now log in using their Google account. Closes #45

September 17, 2024 · 1 min · 69 words · Similarityoung