新增 SaOAuth2DataResolver 数据解析器,负责 Web 交互层面的数据进出

This commit is contained in:
click33
2024-08-13 15:04:39 +08:00
parent b1e2e8a526
commit ae87b11d43
13 changed files with 270 additions and 57 deletions

View File

@@ -15,6 +15,8 @@
*/
package cn.dev33.satoken.util;
import cn.dev33.satoken.SaManager;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -150,7 +152,42 @@ public class SaResult extends LinkedHashMap<String, Object> implements Serializa
}
return this;
}
/**
* 写入一个 json 字符串, 连缀风格
* @param jsonString json 字符串
* @return 对象自身
*/
public SaResult setJsonString(String jsonString) {
Map<String, Object> map = SaManager.getSaJsonTemplate().parseJsonToMap(jsonString);
return setMap(map);
}
/**
* 移除默认属性code、msg、data, 连缀风格
* @return 对象自身
*/
public SaResult removeDefaultFields() {
this.remove("code");
this.remove("msg");
this.remove("data");
return this;
}
/**
* 移除非默认属性code、msg、data, 连缀风格
* @return 对象自身
*/
public SaResult removeNonDefaultFields() {
for (String key : this.keySet()) {
if("code".equals(key) || "msg".equals(key) || "data".equals(key)) {
continue;
}
this.remove(key);
}
return this;
}
// ============================ 静态方法快速构建 ==================================
@@ -180,7 +217,11 @@ public class SaResult extends LinkedHashMap<String, Object> implements Serializa
public static SaResult get(int code, String msg, Object data) {
return new SaResult(code, msg, data);
}
// 构建一个空的
public static SaResult empty() {
return new SaResult();
}
/* (non-Javadoc)
* @see java.lang.Object#toString()

View File

@@ -1,17 +1,15 @@
package com.pj.oauth2;
import javax.servlet.http.HttpServletRequest;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import com.ejlchina.okhttps.OkHttps;
import com.pj.utils.SoMap;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import com.ejlchina.okhttps.OkHttps;
import com.pj.utils.SoMap;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import javax.servlet.http.HttpServletRequest;
/**
* Sa-OAuth2 Client端 控制器
@@ -21,9 +19,9 @@ import cn.dev33.satoken.util.SaResult;
public class SaOAuthClientController {
// 相关参数配置
private String clientId = "1001"; // 应用id
private String clientSecret = "aaaa-bbbb-cccc-dddd-eeee"; // 应用秘钥
private String serverUrl = "http://sa-oauth-server.com:8001"; // 服务端接口
private final String clientId = "1001"; // 应用id
private final String clientSecret = "aaaa-bbbb-cccc-dddd-eeee"; // 应用秘钥
private final String serverUrl = "http://sa-oauth-server.com:8001"; // 服务端接口
// 进入首页
@RequestMapping("/")
@@ -52,14 +50,13 @@ public class SaOAuthClientController {
return SaResult.error(so.getString("msg"));
}
// 根据openid获取其对应的userId
SoMap data = so.getMap("data");
long uid = getUserIdByOpenid(data.getString("openid"));
data.set("uid", uid);
// 根据openid获取其对应的userId
long uid = getUserIdByOpenid(so.getString("openid"));
so.set("uid", uid);
// 返回相关参数
StpUtil.login(uid);
return SaResult.data(data);
return SaResult.data(so);
}
// 根据 Refresh-Token 去刷新 Access-Token
@@ -82,9 +79,8 @@ public class SaOAuthClientController {
return SaResult.error(so.getString("msg"));
}
// 返回相关参数 (data=新的Access-Token )
SoMap data = so.getMap("data");
return SaResult.data(data);
// 返回相关参数
return SaResult.data(so);
}
// 模式三:密码式-授权登录
@@ -108,14 +104,13 @@ public class SaOAuthClientController {
return SaResult.error(so.getString("msg"));
}
// 根据openid获取其对应的userId
SoMap data = so.getMap("data");
long uid = getUserIdByOpenid(data.getString("openid"));
data.set("uid", uid);
// 根据openid获取其对应的userId
long uid = getUserIdByOpenid(so.getString("openid"));
so.set("uid", uid);
// 返回相关参数
StpUtil.login(uid);
return SaResult.data(data);
return SaResult.data(so);
}
// 模式四:获取应用的 Client-Token
@@ -137,9 +132,8 @@ public class SaOAuthClientController {
return SaResult.error(so.getString("msg"));
}
// 返回相关参数 (data=新的Client-Token )
SoMap data = so.getMap("data");
return SaResult.data(data);
// 返回相关参数
return SaResult.data(so);
}
// 注销登录
@@ -166,9 +160,8 @@ public class SaOAuthClientController {
return SaResult.error(so.getString("msg"));
}
// 返回相关参数 (data=获取到的资源 )
SoMap data = so.getMap("data");
return SaResult.data(data);
// 返回相关参数 (data=获取到的资源 )
return SaResult.data(so);
}
// 全局异常拦截

View File

@@ -58,7 +58,7 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 热刷新 -->
<!--<dependency>
<groupId>org.springframework.boot</groupId>

View File

@@ -1,6 +1,6 @@
package com.pj.oauth2;
import cn.dev33.satoken.oauth2.dataloader.SaOAuth2DataLoader;
import cn.dev33.satoken.oauth2.data.loader.SaOAuth2DataLoader;
import cn.dev33.satoken.oauth2.model.SaClientModel;
import org.springframework.stereotype.Component;

View File

@@ -80,12 +80,13 @@ public class SaOAuth2ServerController {
// 模拟账号信息 (真实环境需要查询数据库获取信息)
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("userId", "10008");
map.put("nickname", "shengzhang_");
map.put("avatar", "http://xxx.com/1.jpg");
map.put("age", "18");
map.put("sex", "");
map.put("address", "山东省 青岛市 城阳区");
return SaResult.data(map);
return SaResult.ok().setMap(map);
}
}

View File

@@ -16,8 +16,10 @@
package cn.dev33.satoken.oauth2;
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
import cn.dev33.satoken.oauth2.dataloader.SaOAuth2DataLoader;
import cn.dev33.satoken.oauth2.dataloader.SaOAuth2DataLoaderDefaultImpl;
import cn.dev33.satoken.oauth2.data.loader.SaOAuth2DataLoader;
import cn.dev33.satoken.oauth2.data.loader.SaOAuth2DataLoaderDefaultImpl;
import cn.dev33.satoken.oauth2.data.resolver.SaOAuth2DataResolver;
import cn.dev33.satoken.oauth2.data.resolver.SaOAuth2DataResolverDefaultImpl;
/**
* Sa-Token-OAuth2 模块 总控类
@@ -64,4 +66,22 @@ public class SaOAuth2Manager {
SaOAuth2Manager.dataLoader = dataLoader;
}
/**
* OAuth2 数据解析器 Bean
*/
private static volatile SaOAuth2DataResolver dataResolver;
public static SaOAuth2DataResolver getDataResolver() {
if (dataResolver == null) {
synchronized (SaOAuth2Manager.class) {
if (dataResolver == null) {
setDataResolver(new SaOAuth2DataResolverDefaultImpl());
}
}
}
return dataResolver;
}
public static void setDataResolver(SaOAuth2DataResolver dataResolver) {
SaOAuth2Manager.dataResolver = dataResolver;
}
}

View File

@@ -77,7 +77,22 @@ public class SaOAuth2Consts {
public static String client_credentials = "client_credentials";
public static String implicit = "implicit";
}
/**
* 所有 token 类型
*/
public static final class TokenType {
// 全小写
public static String basic = "basic";
public static String digest = "digest";
public static String bearer = "bearer";
// 首字母大写
public static String Basic = "Basic";
public static String Digest = "Digest";
public static String Bearer = "Bearer";
}
/** 表示OK的返回结果 */
public static final String OK = "ok";

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.oauth2.dataloader;
package cn.dev33.satoken.oauth2.data.loader;
import cn.dev33.satoken.oauth2.model.SaClientModel;

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.oauth2.dataloader;
package cn.dev33.satoken.oauth2.data.loader;
/**
* Sa-Token OAuth2 数据加载器 默认实现类

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.oauth2.data.resolver;
import cn.dev33.satoken.oauth2.model.AccessTokenModel;
import cn.dev33.satoken.oauth2.model.ClientTokenModel;
import cn.dev33.satoken.util.SaResult;
import java.util.Map;
/**
* Sa-Token OAuth2 数据解析器,负责 Web 交互层面的数据进出:
* <p>1、从请求中按照指定格式读取数据</p>
* <p>2、构建数据输出格式</p>
*
* @author click33
* @since 1.39.0
*/
public interface SaOAuth2DataResolver {
/**
* 构建返回值: 获取 token
* @param at token信息
* @return /
*/
Map<String, Object> buildTokenReturnValue(AccessTokenModel at);
/**
* 构建返回值: RefreshToken 刷新 Access-Token
* @param at token信息
* @return /
*/
default Map<String, Object> buildRefreshTokenReturnValue(AccessTokenModel at) {
return buildTokenReturnValue(at);
}
/**
* 构建返回值: 回收 Access-Token
* @return /
*/
default Map<String, Object> buildRevokeTokenReturnValue() {
return SaResult.ok();
}
/**
* 构建返回值: password 模式认证 获取 token
* @param at token信息
* @return /
*/
default Map<String, Object> buildPasswordReturnValue(AccessTokenModel at) {
return buildTokenReturnValue(at);
}
/**
* 构建返回值: 凭证式 模式认证 获取 token
* @param ct token信息
*/
Map<String, Object> buildClientTokenReturnValue(ClientTokenModel ct);
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2020-2099 sa-token.cc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.dev33.satoken.oauth2.data.resolver;
import cn.dev33.satoken.oauth2.consts.SaOAuth2Consts.TokenType;
import cn.dev33.satoken.oauth2.model.AccessTokenModel;
import cn.dev33.satoken.oauth2.model.ClientTokenModel;
import cn.dev33.satoken.util.SaResult;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Sa-Token OAuth2 数据解析器,负责 Web 交互层面的数据进出:
* <p>1、从请求中按照指定格式读取数据</p>
* <p>2、构建数据输出格式</p>
*
* @author click33
* @since 1.39.0
*/
public class SaOAuth2DataResolverDefaultImpl implements SaOAuth2DataResolver {
/**
* 构建返回值: 获取 token
*/
public Map<String, Object> buildTokenReturnValue(AccessTokenModel at) {
Map<String, Object> map = new LinkedHashMap<>();
map.put("token_type", TokenType.bearer);
map.put("access_token", at.accessToken);
map.put("refresh_token", at.refreshToken);
map.put("expires_in", at.getExpiresIn());
map.put("refresh_expires_in", at.getRefreshExpiresIn());
map.put("client_id", at.clientId);
map.put("scope", at.scope);
map.put("openid", at.openid);
return SaResult.ok().setMap(map);
}
/**
* 构建返回值: password 模式认证 获取 token
*/
@Override
public Map<String, Object> buildClientTokenReturnValue(ClientTokenModel ct) {
Map<String, Object> map = new LinkedHashMap<>();
map.put("client_token", ct.clientToken);
map.put("access_token", ct.clientToken); // 兼容 OAuth2 协议
map.put("expires_in", ct.getExpiresIn());
map.put("client_id", ct.clientId);
map.put("scope", ct.scope);
return SaResult.ok().setMap(map);
}
}

View File

@@ -61,7 +61,6 @@ public class SaOAuth2ServerProcessor {
// 获取变量
SaRequest req = SaHolder.getRequest();
SaResponse res = SaHolder.getResponse();
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
// ------------------ 路由分发 ------------------
@@ -187,14 +186,13 @@ public class SaOAuth2ServerProcessor {
public Object token() {
// 获取变量
SaRequest req = SaHolder.getRequest();
SaResponse res = SaHolder.getResponse();
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
// 获取参数
String authorizationValue = SaHttpBasicUtil.getAuthorizationValue();
String clientId;
String clientSecret;
// gitlab回调token接口时,按照的是标准的oauth2协议的basic请求头,basic中会包含client_id和client_secret的信息
// gitlab 回调 token 接口时,按照的是标准的oauth2协议的basic请求头,basic中会包含client_id和client_secret的信息
if(SaFoxUtil.isEmpty(authorizationValue)){
clientId = req.getParamNotNull(Param.client_id);
clientSecret = req.getParamNotNull(Param.client_secret);
@@ -211,10 +209,10 @@ public class SaOAuth2ServerProcessor {
oauth2Template.checkGainTokenParam(code, clientId, clientSecret, redirectUri);
// 构建 Access-Token
AccessTokenModel token = oauth2Template.generateAccessToken(code);
AccessTokenModel accessTokenModel = oauth2Template.generateAccessToken(code);
// 返回
return SaResult.data(token.toLineMap());
return SaOAuth2Manager.getDataResolver().buildTokenReturnValue(accessTokenModel);
}
/**
@@ -233,9 +231,11 @@ public class SaOAuth2ServerProcessor {
// 校验参数
oauth2Template.checkRefreshTokenParam(clientId, clientSecret, refreshToken);
// 获取新Token返回
Object data = oauth2Template.refreshAccessToken(refreshToken).toLineMap();
return SaResult.data(data);
// 获取新 Access-Token
AccessTokenModel accessTokenModel = oauth2Template.refreshAccessToken(refreshToken);
// 返回
return SaOAuth2Manager.getDataResolver().buildRefreshTokenReturnValue(accessTokenModel);
}
/**
@@ -261,7 +261,9 @@ public class SaOAuth2ServerProcessor {
// 回收 Access-Token
oauth2Template.revokeAccessToken(accessToken);
return SaResult.ok();
// 返回
return SaOAuth2Manager.getDataResolver().buildRevokeTokenReturnValue();
}
/**
@@ -271,7 +273,6 @@ public class SaOAuth2ServerProcessor {
public Object doLogin() {
// 获取变量
SaRequest req = SaHolder.getRequest();
SaResponse res = SaHolder.getResponse();
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
return cfg.getDoLoginHandle().apply(req.getParamNotNull(Param.name), req.getParamNotNull(Param.pwd));
@@ -330,7 +331,7 @@ public class SaOAuth2ServerProcessor {
AccessTokenModel at = oauth2Template.generateAccessToken(ra, true);
// 6、返回 Access-Token
return SaResult.data(at.toLineMap());
return SaOAuth2Manager.getDataResolver().buildPasswordReturnValue(at);
}
/**
@@ -340,8 +341,6 @@ public class SaOAuth2ServerProcessor {
public Object clientToken() {
// 获取变量
SaRequest req = SaHolder.getRequest();
SaResponse res = SaHolder.getResponse();
SaOAuth2Config cfg = SaOAuth2Manager.getConfig();
// 获取参数
String clientId = req.getParamNotNull(Param.client_id);
@@ -354,11 +353,11 @@ public class SaOAuth2ServerProcessor {
// 校验 ClientSecret
oauth2Template.checkClientSecret(clientId, clientSecret);
// 返回 Client-Token
// 生成
ClientTokenModel ct = oauth2Template.generateClientToken(clientId, scope);
// 返回 Client-Token
return SaResult.data(ct.toLineMap());
// 返回
return SaOAuth2Manager.getDataResolver().buildClientTokenReturnValue(ct);
}
/**

View File

@@ -17,7 +17,7 @@ package cn.dev33.satoken.spring.oauth2;
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
import cn.dev33.satoken.oauth2.dataloader.SaOAuth2DataLoader;
import cn.dev33.satoken.oauth2.data.loader.SaOAuth2DataLoader;
import cn.dev33.satoken.oauth2.processor.SaOAuth2ServerProcessor;
import cn.dev33.satoken.oauth2.template.SaOAuth2Template;
import org.springframework.beans.factory.annotation.Autowired;