在之前的文章中实现了一个认证服务,并且添加了一些自定义的内容,现在暂时没想到认证服务的新内容,本篇文章就先写一下客户端对接的吧,水一篇。
当用户通过客户端去访问一个受限的资源时,客户端会检测是否有登录信息,没有登录信息会重定向至认证服务器去请求授权,认证服务器会检测是否有登录信息(检查session),检测到没有登录则重定向至登录页面返回给用户,用户输入账号密码后提交,认证服务器认证以后会重定向至授权接口,授权接口生成一个code之后携带code重定向至客户端配置的redirect_uri,Security OAuth2 Client默认实现了一个处理回调的接口,会自动使用code获取token,地址为:/login/oauth2/code/*,最后的*要填配置客户端的registrationId,后边会提到;然后该接口请求认证服务去获取一个access_token,用access_token换取用户信息,框架会将token的信息存入session中,以后再发起请求时会从session中获取token。
客户端这里就是一个独立的项目了,跟之前的认证服务没有什么关联,读者可自选自己使用的Spring Boot版本,各版本的oauth2 client版本的对接大差不差,基本上差不多,可能实现会有所不同,但基本都一样的。在对接过程中我会放一些文档,读者可以去文档中找对应版本的文档去编写代码。
使用idea或者在Spring Initializr创建一个SpringBoot项目;创建时引用oauth2-client和web依赖。
4.0.0 org.springframework.boot spring-boot-starter-parent 3.1.0 com.example authorization-client-example 0.0.1-SNAPSHOT authorization-client-example authorization-client-example 17 org.springframework.boot spring-boot-starter-oauth2-client org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin
server: # 修改端口 port: 8000 spring: security: oauth2: client: provider: # 认证提供者,自定义名称 custom-issuer: # Token签发地址(认证服务地址) issuer-uri: http://192.168.120.33:8080 # 获取用户信息的地址,默认的/userinfo端点需要IdToken获取,为避免麻烦自定一个用户信息接口 user-info-uri: ${spring.security.oauth2.client.provider.custom-issuer.issuer-uri}/user registration: messaging-client-oidc: # oauth认证提供者配置,和上边配置的认证提供者关联起来 provider: custom-issuer # 客户端名称,自定义 client-name: message-client # 客户端id,从认证服务申请的客户端id client-id: messaging-client # 客户端秘钥 client-secret: 123456 # 客户端认证方式 client-authentication-method: client_secret_basic # 获取Token使用的授权流程 authorization-grant-type: authorization_code # 回调地址,这里设置为Spring Security Client默认实现使用code换取token的接口 redirect-uri: http://127.0.0.1:8000/login/oauth2/code/messaging-client-oidc scope: - message.read - message.write
更多详细的客户端配置信息移步官网文档
文档中配置权限范围的字段是scopes,但是在代码中是scope,这里可能是文档中没更新吧,大家在参阅文档时需要注意一下代码中具体的属性名是什么。
注意:认证服务器和客户端在同一个机器上时不能使用同一个ip,例如127.0.0.1,在存储cookie时不会区分端口的,比如127.0.0.1:8000和127.0.0.1:8080这两个,他们的cookie是同一个的,后者会覆盖前者;如果配置认证服务的地址是127.0.0.1:8080然后通过127.0.0.1:8000去访问客户端则会在登录后出现[authorization_request_not_found]异常,详见spring-security issues 5946
在AuthorizationController中添加/user接口
@ResponseBody @GetMapping("/user") public Mapuser(Principal principal) { if (!(principal instanceof JwtAuthenticationToken token)) { return Collections.emptyMap(); } return token.getToken().getClaims(); }
综上所述,一个很简单的客户端已经配置完成了。
直接将认证服务的测试接口copy过来
package com.example.controller; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * 测试接口 * * @author vains */ @RestController public class TestController { @GetMapping("/test01") @PreAuthorize("hasAuthority('SCOPE_message.read')") public String test01() { return "test01"; } @GetMapping("/test02") @PreAuthorize("hasAuthority('SCOPE_message.write')") public String test02() { return "test02"; } @GetMapping("/app") @PreAuthorize("hasAuthority('app')") public String app() { return "app"; } }
1. 启动认证服务
2. 启动客户端服务
3. 访问客户端的/app接口
看到这里可能有些读者会比较疑惑,比如框架怎么知道认证服务器授权接口的地址?框架怎么知道认证服务获取token的接口?
项目在初始化时会根据配置的issuer-uri拼接url,即签发地址根目录/.well-known/openid-configuration,像项目中配置的就是http://192.168.120.33:8080/.well-known/openid-configuration ;该接口会返回认证服务器的元信息,如下:
所以通过该接口就可以很清楚的知道各授权端点的请求路径。
默认是基于内存的,基本上来说服务注册的客户端不会很多,基于内存也就够用了;框架也提供了基于db的方式,需要自己注入一个JdbcOAuth2AuthorizedClientService;让客户端认证信息持久化。文档
sql位置
客户端在oauth2角色解释中是第三方的一个应用,一般会配合资源服务一起使用
会同时添加资源服务依赖,客户端负责调用认证服务登录,资源服务器负责解析获取到的token,然后获取token中的权限,目前token中的权限只有scope的权限,并且不太好自定义,所以就需要通过资源服务器配置去更好的解析token。
本篇文章以较少的代码集成了Security OAuth2 Client,体验到了springboot最开始说的约定大于配置的好处,框架添加了大量的默认配置,只需更改必须修改的自定义部分即可,本次的代码部分只有更改yml和编写一个测试接口,其它的重定向至认证服务和获取token的配置都已经默认实现了。
下篇会记录一下资源客户端的集成。
代码已提交至Gitee:https://gitee.com/vains-Sofia/authorization-example