在之前的文章中实现了一个认证服务,并且添加了一些自定义的内容,现在暂时没想到认证服务的新内容,本篇文章就先写一下客户端对接的吧,水一篇。

当用户通过客户端去访问一个受限的资源时,客户端会检测是否有登录信息,没有登录信息会重定向至认证服务器去请求授权,认证服务器会检测是否有登录信息(检查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 Map user(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