diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/config/SaSsoConfig.java b/sa-token-core/src/main/java/cn/dev33/satoken/config/SaSsoConfig.java index 93539bff..7562e3ed 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/config/SaSsoConfig.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/config/SaSsoConfig.java @@ -1,5 +1,6 @@ package cn.dev33.satoken.config; +import java.io.Serializable; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; @@ -13,7 +14,9 @@ import cn.dev33.satoken.util.SaResult; * @author kong * */ -public class SaSsoConfig { +public class SaSsoConfig implements Serializable { + + private static final long serialVersionUID = -6541180061782004705L; /** * Ticket有效期 (单位: 秒) diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java b/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java index 2823d9c1..9e1b4d57 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/config/SaTokenConfig.java @@ -1,5 +1,7 @@ package cn.dev33.satoken.config; +import java.io.Serializable; + /** * Sa-Token 配置类 Model *

@@ -8,7 +10,9 @@ package cn.dev33.satoken.config; * @author kong * */ -public class SaTokenConfig { +public class SaTokenConfig implements Serializable { + + private static final long serialVersionUID = -6541180061782004705L; /** token名称 (同时也是cookie名称) */ private String tokenName = "satoken"; diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/context/model/SaRequest.java b/sa-token-core/src/main/java/cn/dev33/satoken/context/model/SaRequest.java index 76b7dfa6..67b8fbab 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/context/model/SaRequest.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/context/model/SaRequest.java @@ -82,6 +82,14 @@ public interface SaRequest { */ public String getRequestPath(); + /** + * 返回当前请求path是否为指定值 + * @return see note + */ + public default boolean isPath(String path) { + return getRequestPath().equals(path); + } + /** * 返回当前请求的url,例:http://xxx.com/?id=127 * @return see note diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/sso/SaSsoConsts.java b/sa-token-core/src/main/java/cn/dev33/satoken/sso/SaSsoConsts.java index 1bc440ed..b79a2489 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/sso/SaSsoConsts.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/sso/SaSsoConsts.java @@ -65,7 +65,7 @@ public class SaSsoConsts { /** 表示OK的返回结果 */ public static final String OK = "ok"; - /** 表示请求没有得到任何有效处理 */ - public static final String NOT_HANDLE = "not handle"; + /** 表示请求没有得到任何有效处理 {msg: "not handle"} */ + public static final String NOT_HANDLE = "{\"msg\": \"not handle\"}"; } diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java index 6cdf98dc..249404d4 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaFoxUtil.java @@ -356,4 +356,43 @@ public class SaFoxUtil { } } + /** + * 将指定字符串按照逗号分隔符转化为字符串集合 + * @param str 字符串 + * @return 分割后的字符串集合 + */ + public static List convertStringToList(String str) { + List list = new ArrayList(); + if(isEmpty(str)) { + return list; + } + String[] arr = str.split(","); + for (String s : arr) { + s = s.trim(); + if(isEmpty(s) == false) { + list.add(s); + } + } + return list; + } + + /** + * 将指定集合按照逗号连接成一个字符串 + * @param list 集合 + * @return 字符串 + */ + public static String convertListToString(List list) { + if(list == null || list.size() == 0) { + return ""; + } + String str = ""; + for (int i = 0; i < list.size(); i++) { + str += list.get(i); + if(i != list.size() - 1) { + str += ","; + } + } + return str; + } + } diff --git a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaResult.java b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaResult.java index 6bfe2391..51222077 100644 --- a/sa-token-core/src/main/java/cn/dev33/satoken/util/SaResult.java +++ b/sa-token-core/src/main/java/cn/dev33/satoken/util/SaResult.java @@ -1,67 +1,107 @@ package cn.dev33.satoken.util; import java.io.Serializable; - +import java.util.LinkedHashMap; +import java.util.Map; /** - * 对Ajax请求返回Json格式数据的简易封装 + * 对Ajax请求返回Json格式数据的简易封装
+ * 所有预留字段:
+ * code=状态码
+ * msg=描述信息
+ * data=携带对象
* @author kong * */ -public class SaResult implements Serializable{ +public class SaResult extends LinkedHashMap implements Serializable{ - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; // 序列化版本号 public static final int CODE_SUCCESS = 200; public static final int CODE_ERROR = 500; - /** - * 状态码 - */ - public int code; + public SaResult(int code, String msg, Object data) { + this.setCode(code); + this.setMsg(msg); + this.setData(data); + } /** - * 描述信息 + * 获取code + * @return code */ - public String msg; - + public Integer getCode() { + return (Integer)this.get("code"); + } /** - * 携带对象 + * 获取msg + * @return msg */ - public Object data; - + public String getMsg() { + return (String)this.get("msg"); + } + /** + * 获取data + * @return data + */ + public Object getData() { + return (Object)this.get("data"); + } + /** * 给code赋值,连缀风格 + * @param code code + * @return 对象自身 */ public SaResult setCode(int code) { - this.code = code; + this.put("code", code); return this; } - /** * 给msg赋值,连缀风格 + * @param msg msg + * @return 对象自身 */ public SaResult setMsg(String msg) { - this.msg = msg; + this.put("msg", msg); + return this; + } + /** + * 给data赋值,连缀风格 + * @param data data + * @return 对象自身 + */ + public SaResult setData(Object data) { + this.put("data", data); return this; } /** - * 给data赋值,连缀风格 + * 写入一个值 自定义key, 连缀风格 + * @param key key + * @param data data + * @return 对象自身 */ - public SaResult setData(Object data) { - this.data = data; + public SaResult set(String key, Object data) { + this.put(key, data); return this; } - // ============================ 构建 ================================== - - public SaResult(int code, String msg, Object data) { - this.code = code; - this.msg = msg; - this.data = data; + /** + * 写入一个Map, 连缀风格 + * @param map map + * @return 对象自身 + */ + public SaResult setMap(Map map) { + for (String key : map.keySet()) { + this.put(key, map.get(key)); + } + return this; } + + // ============================ 构建 ================================== + // 构建成功 public static SaResult ok() { return new SaResult(CODE_SUCCESS, "ok", null); @@ -93,9 +133,9 @@ public class SaResult implements Serializable{ @Override public String toString() { return "{" - + "\"code\": " + this.code - + ", \"msg\": \"" + this.msg + "\"" - + ", \"data\": \"" + this.data + "\"" + + "\"code\": " + this.getCode() + + ", \"msg\": \"" + this.getMsg() + "\"" + + ", \"data\": \"" + this.getData() + "\"" + "}"; } diff --git a/sa-token-demo/sa-token-demo-oauth2-client/pom.xml b/sa-token-demo/sa-token-demo-oauth2-client/pom.xml index e2d6d24d..978d9856 100644 --- a/sa-token-demo/sa-token-demo-oauth2-client/pom.xml +++ b/sa-token-demo/sa-token-demo-oauth2-client/pom.xml @@ -22,26 +22,39 @@ - + org.springframework.boot spring-boot-starter-web - + cn.dev33 sa-token-spring-boot-starter ${sa-token-version} + + + + org.springframework.boot + spring-boot-starter-thymeleaf + - com.ejlchina - okhttps - 2.4.5 + com.ejlchina + okhttps + 3.1.1 - + + + + org.springframework.boot + spring-boot-devtools + true + + org.springframework.boot diff --git a/sa-token-demo/sa-token-demo-oauth2-client/src/main/java/com/pj/SaOAuth2ClientApplication.java b/sa-token-demo/sa-token-demo-oauth2-client/src/main/java/com/pj/SaOAuth2ClientApplication.java index 1a10adcf..4853ef7b 100644 --- a/sa-token-demo/sa-token-demo-oauth2-client/src/main/java/com/pj/SaOAuth2ClientApplication.java +++ b/sa-token-demo/sa-token-demo-oauth2-client/src/main/java/com/pj/SaOAuth2ClientApplication.java @@ -3,8 +3,9 @@ package com.pj; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; + /** - * 启动 + * 启动:OAuth2-Client端 * @author kong */ @SpringBootApplication @@ -12,7 +13,13 @@ public class SaOAuth2ClientApplication { public static void main(String[] args) { SpringApplication.run(SaOAuth2ClientApplication.class, args); - System.out.println("\n客户端启动成功,访问: http://localhost:8002/login.html"); + System.out.println("\nSa-Token-OAuth Client端启动成功\n\n" + str); } + + static String str = "首先在host文件 (C:\\WINDOWS\\system32\\drivers\\etc\\hosts) 添加以下内容: \r\n" + + " 127.0.0.1 sa-oauth-server.com \r\n" + + " 127.0.0.1 sa-oauth-client.com \r\n" + + "再从浏览器访问:\r\n" + + " http://sa-oauth-client.com:8002"; } diff --git a/sa-token-demo/sa-token-demo-oauth2-client/src/main/java/com/pj/oauth2/SaOAuthClientController.java b/sa-token-demo/sa-token-demo-oauth2-client/src/main/java/com/pj/oauth2/SaOAuthClientController.java new file mode 100644 index 00000000..e0cb545e --- /dev/null +++ b/sa-token-demo/sa-token-demo-oauth2-client/src/main/java/com/pj/oauth2/SaOAuthClientController.java @@ -0,0 +1,188 @@ +package com.pj.oauth2; + +import javax.servlet.http.HttpServletRequest; + +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; + +/** + * Sa-OAuth2 Client端 控制器 + * @author kong + */ +@RestController +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"; // 服务端接口 + + // 进入首页 + @RequestMapping("/") + public Object index(HttpServletRequest request) { + request.setAttribute("uid", StpUtil.getLoginIdDefaultNull()); + return new ModelAndView("index.html"); + } + + // 根据Code码进行登录,获取 Access-Token 和 openid + @RequestMapping("/codeLogin") + public SaResult codeLogin(String code) { + // 调用Server端接口,获取 Access-Token 以及其他信息 + String str = OkHttps.sync(serverUrl + "/oauth2/token") + .addBodyPara("grant_type", "authorization_code") + .addBodyPara("code", code) + .addBodyPara("client_id", clientId) + .addBodyPara("client_secret", clientSecret) + .post() + .getBody() + .toString(); + SoMap so = SoMap.getSoMap().setJsonString(str); + System.out.println("返回结果: " + so); + + // code不等于200 代表请求失败 + if(so.getInt("code") != 200) { + return SaResult.error(so.getString("msg")); + } + + // 根据openid获取其对应的userId + SoMap data = so.getMap("data"); + long uid = getUserIdByOpenid(data.getString("openid")); + data.set("uid", uid); + + // 返回相关参数 + StpUtil.login(uid); + return SaResult.data(data); + } + + // 根据 Refresh-Token 去刷新 Access-Token + @RequestMapping("/refresh") + public SaResult refresh(String refreshToken) { + // 调用Server端接口,通过 Refresh-Token 刷新出一个新的 Access-Token + String str = OkHttps.sync(serverUrl + "/oauth2/refresh") + .addBodyPara("grant_type", "refresh_token") + .addBodyPara("client_id", clientId) + .addBodyPara("client_secret", clientSecret) + .addBodyPara("refresh_token", refreshToken) + .post() + .getBody() + .toString(); + SoMap so = SoMap.getSoMap().setJsonString(str); + System.out.println("返回结果: " + so); + + // code不等于200 代表请求失败 + if(so.getInt("code") != 200) { + return SaResult.error(so.getString("msg")); + } + + // 返回相关参数 (data=新的Access-Token ) + SoMap data = so.getMap("data"); + return SaResult.data(data); + } + + // 模式三:密码式-授权登录 + @RequestMapping("/passwordLogin") + public SaResult passwordLogin(String username, String password) { + // 模式三:密码式-授权登录 + String str = OkHttps.sync(serverUrl + "/oauth2/token") + .addBodyPara("grant_type", "password") + .addBodyPara("client_id", clientId) + .addBodyPara("username", username) + .addBodyPara("password", password) + .post() + .getBody() + .toString(); + SoMap so = SoMap.getSoMap().setJsonString(str); + System.out.println("返回结果: " + so); + + // code不等于200 代表请求失败 + if(so.getInt("code") != 200) { + return SaResult.error(so.getString("msg")); + } + + // 根据openid获取其对应的userId + SoMap data = so.getMap("data"); + long uid = getUserIdByOpenid(data.getString("openid")); + data.set("uid", uid); + + // 返回相关参数 + StpUtil.login(uid); + return SaResult.data(data); + } + + // 模式四:获取应用的 Client-Token + @RequestMapping("/clientToken") + public SaResult clientToken() { + // 调用Server端接口 + String str = OkHttps.sync(serverUrl + "/oauth2/client_token") + .addBodyPara("grant_type", "client_credentials") + .addBodyPara("client_id", clientId) + .addBodyPara("client_secret", clientSecret) + .post() + .getBody() + .toString(); + SoMap so = SoMap.getSoMap().setJsonString(str); + System.out.println("返回结果: " + so); + + // code不等于200 代表请求失败 + if(so.getInt("code") != 200) { + return SaResult.error(so.getString("msg")); + } + + // 返回相关参数 (data=新的Client-Token ) + SoMap data = so.getMap("data"); + return SaResult.data(data); + } + + // 注销登录 + @RequestMapping("/logout") + public SaResult logout() { + StpUtil.logout(); + return SaResult.ok(); + } + + // 根据 Access-Token 置换相关的资源: 获取账号昵称、头像、性别等信息 + @RequestMapping("/getUserinfo") + public SaResult getUserinfo(String accessToken) { + // 调用Server端接口,查询开放的资源 + String str = OkHttps.sync(serverUrl + "/oauth2/userinfo") + .addBodyPara("access_token", accessToken) + .post() + .getBody() + .toString(); + SoMap so = SoMap.getSoMap().setJsonString(str); + System.out.println("返回结果: " + so); + + // code不等于200 代表请求失败 + if(so.getInt("code") != 200) { + return SaResult.error(so.getString("msg")); + } + + // 返回相关参数 (data=获取到的资源 ) + SoMap data = so.getMap("data"); + return SaResult.data(data); + } + + // 全局异常拦截 + @ExceptionHandler + public SaResult handlerException(Exception e) { + e.printStackTrace(); + return SaResult.error(e.getMessage()); + } + + + // ------------ 模拟方法 ------------------ + // 模拟方法:根据openid获取userId + private long getUserIdByOpenid(String openid) { + // 此方法仅做模拟,实际开发要根据具体业务逻辑来获取userId + return 10001; + } + +} diff --git a/sa-token-demo/sa-token-demo-oauth2-client/src/main/java/com/pj/utils/SoMap.java b/sa-token-demo/sa-token-demo-oauth2-client/src/main/java/com/pj/utils/SoMap.java index e4524216..5258a6d3 100644 --- a/sa-token-demo/sa-token-demo-oauth2-client/src/main/java/com/pj/utils/SoMap.java +++ b/sa-token-demo/sa-token-demo-oauth2-client/src/main/java/com/pj/utils/SoMap.java @@ -33,12 +33,10 @@ public class SoMap extends LinkedHashMap { public SoMap() { } - /** 以下元素会在isNull函数中被判定为Null, */ public static final Object[] NULL_ELEMENT_ARRAY = {null, ""}; public static final List NULL_ELEMENT_LIST; - static { NULL_ELEMENT_LIST = Arrays.asList(NULL_ELEMENT_ARRAY); } @@ -144,6 +142,22 @@ public class SoMap extends LinkedHashMap { return getDateByFormat(key, "yyyy-MM-dd HH:mm:ss"); } + /** 转为Map并返回 */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public SoMap getMap(String key) { + Object value = get(key); + if(value == null) { + return SoMap.getSoMap(); + } + if(value instanceof Map) { + return SoMap.getSoMap((Map)value); + } + if(value instanceof String) { + return SoMap.getSoMap().setJsonString((String)value); + } + throw new RuntimeException("值无法转化为SoMap: " + value); + } + /** 获取集合(必须原先就是个集合,否则会创建个新集合并返回) */ @SuppressWarnings("unchecked") public List getList(String key) { diff --git a/sa-token-demo/sa-token-demo-oauth2-client/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-oauth2-client/src/main/resources/application.yml index b7d430a9..a2331b3c 100644 --- a/sa-token-demo/sa-token-demo-oauth2-client/src/main/resources/application.yml +++ b/sa-token-demo/sa-token-demo-oauth2-client/src/main/resources/application.yml @@ -5,9 +5,3 @@ server: sa-token: # token名称 (同时也是cookie名称) token-name: satoken-client - -spring: - # 静态文件路径映射 - resources: - static-locations: classpath:/META-INF/resources/,classpath:/resources/, classpath:/static/, classpath:/public/ - # static-locations: file:E:\work\project-yun\sa-token\sa-token-demo-oauth2\sa-token-demo-oauth2-client\src\main\resources\static\ diff --git a/sa-token-demo/sa-token-demo-oauth2-client/src/main/resources/templates/index.html b/sa-token-demo/sa-token-demo-oauth2-client/src/main/resources/templates/index.html new file mode 100644 index 00000000..ebaef79a --- /dev/null +++ b/sa-token-demo/sa-token-demo-oauth2-client/src/main/resources/templates/index.html @@ -0,0 +1,253 @@ + + + + + Sa-OAuth2-Client端-测试页 + + + + + + + + + + diff --git a/sa-token-demo/sa-token-demo-oauth2-server/pom.xml b/sa-token-demo/sa-token-demo-oauth2-server/pom.xml index fac12aca..1645da58 100644 --- a/sa-token-demo/sa-token-demo-oauth2-server/pom.xml +++ b/sa-token-demo/sa-token-demo-oauth2-server/pom.xml @@ -22,27 +22,27 @@ - + org.springframework.boot spring-boot-starter-web - + cn.dev33 sa-token-spring-boot-starter ${sa-token-version} - + cn.dev33 sa-token-oauth2 ${sa-token-version} - + cn.dev33 sa-token-dao-redis-jackson @@ -53,6 +53,19 @@ commons-pool2 + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + + org.springframework.boot + spring-boot-devtools + true + + org.springframework.boot diff --git a/sa-token-demo/sa-token-demo-oauth2-server/src/main/java/com/pj/SaOAuth2ServerApplication.java b/sa-token-demo/sa-token-demo-oauth2-server/src/main/java/com/pj/SaOAuth2ServerApplication.java index efc37def..3c2d1567 100644 --- a/sa-token-demo/sa-token-demo-oauth2-server/src/main/java/com/pj/SaOAuth2ServerApplication.java +++ b/sa-token-demo/sa-token-demo-oauth2-server/src/main/java/com/pj/SaOAuth2ServerApplication.java @@ -4,7 +4,7 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** - * 启动 + * 启动:OAuth2-Server端 * @author kong */ @SpringBootApplication @@ -12,7 +12,7 @@ public class SaOAuth2ServerApplication { public static void main(String[] args) { SpringApplication.run(SaOAuth2ServerApplication.class, args); - System.out.println("\nOAuth-Server端启动成功"); + System.out.println("\nSa-Token-OAuth Server端启动成功"); } } diff --git a/sa-token-demo/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2ServerController.java b/sa-token-demo/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2ServerController.java new file mode 100644 index 00000000..683a2df4 --- /dev/null +++ b/sa-token-demo/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2ServerController.java @@ -0,0 +1,89 @@ +package com.pj.oauth2; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +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 cn.dev33.satoken.context.SaHolder; +import cn.dev33.satoken.oauth2.config.SaOAuth2Config; +import cn.dev33.satoken.oauth2.logic.SaOAuth2Handle; +import cn.dev33.satoken.oauth2.logic.SaOAuth2Util; +import cn.dev33.satoken.stp.StpUtil; +import cn.dev33.satoken.util.SaResult; + +/** + * Sa-OAuth2 Server端 控制器 + * @author kong + * + */ +@RestController +public class SaOAuth2ServerController { + + // 处理所有OAuth相关请求 + @RequestMapping("/oauth2/*") + public Object request() { + System.out.println("--------------进入请求 "); + return SaOAuth2Handle.serverRequest(); + } + + // Sa-OAuth2 定制化配置 + @Autowired + public void setSaOAuth2Config(SaOAuth2Config cfg) { + cfg. + // 未登录的视图 + setNotLoginView(()->{ + return new ModelAndView("login.html"); + }). + // 登录处理函数 + setDoLoginHandle((name, pwd) -> { + if("sa".equals(name) && "123456".equals(pwd)) { + StpUtil.login(10001); + return SaResult.ok(); + } + return SaResult.error("账号名或密码错误"); + }). + // 授权确认视图 + setConfirmView((clientId, scope)->{ + Map map = new HashMap<>(); + map.put("clientId", clientId); + map.put("scope", scope); + return new ModelAndView("confirm.html", map); + }) + ; + } + + // 全局异常拦截 + @ExceptionHandler + public SaResult handlerException(Exception e) { + e.printStackTrace(); + return SaResult.error(e.getMessage()); + } + + + // ---------- 开放相关资源接口: Client端根据 Access-Token ,置换相关资源 ------------ + + // 获取Userinfo信息:昵称、头像、性别等等 + @RequestMapping("/oauth2/userinfo") + public SaResult userinfo() { + // 获取 Access-Token 对应的账号id + String accessToken = SaHolder.getRequest().getParamNotNull("access_token"); + Object loginId = SaOAuth2Util.getLoginIdByAccessToken(accessToken); + System.out.println("-------- 此Access-Token对应的账号id: " + loginId); + + // 模拟账号信息 (真实环境需要查询数据库获取信息) + Map map = new LinkedHashMap(); + 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); + } + +} diff --git a/sa-token-demo/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2TemplateImpl.java b/sa-token-demo/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2TemplateImpl.java index d31ead24..471f75b1 100644 --- a/sa-token-demo/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2TemplateImpl.java +++ b/sa-token-demo/sa-token-demo-oauth2-server/src/main/java/com/pj/oauth2/SaOAuth2TemplateImpl.java @@ -1,72 +1,38 @@ package com.pj.oauth2; -import java.util.Arrays; -import java.util.List; - import org.springframework.stereotype.Component; import cn.dev33.satoken.oauth2.logic.SaOAuth2Template; +import cn.dev33.satoken.oauth2.model.SaClientModel; /** - * 使用oauth2.0 所必须的一些自定义实现 + * Sa-Token OAuth2.0 整合实现 * @author kong */ @Component public class SaOAuth2TemplateImpl extends SaOAuth2Template { - - /* - * ------ 注意: 以下代码均为示例,真实环境需要根据数据库查询相关信息 - */ - - // 返回此平台所有权限集合 + // 根据 id 获取 Client 信息 @Override - public List getAppScopeList() { - return Arrays.asList("userinfo"); + public SaClientModel getClientModel(String clientId) { + // 此为模拟数据,真实环境需要从数据库查询 + if("1001".equals(clientId)) { + return new SaClientModel() + .setClientId("10001") + .setClientSecret("aaaa-bbbb-cccc-dddd-eeee") + .setAllowUrl("*") + .setContractScope("userinfo"); + } + return null; } - - // 返回指定Client签约的所有Scope集合 - @Override - public List getClientScopeList(String clientId) { - return Arrays.asList("userinfo"); - } - - // 获取指定 LoginId 对指定 Client 已经授权过的所有 Scope - @Override - public List getGrantScopeList(Object loginId, String clientId) { - return Arrays.asList(); - } - - // 返回指定Client允许的回调域名, 多个用逗号隔开, *代表不限制 - @Override - public String getClientDomain(String clientId) { - return "*"; - } - - // 返回指定ClientId的ClientSecret - @Override - public String getClientSecret(String clientId) { - return "aaaa-bbbb-cccc-dddd-eeee"; - } - - // 根据ClientId和LoginId返回openid + + // 根据ClientId 和 LoginId 获取openid @Override public String getOpenid(String clientId, Object loginId) { + // 此为模拟数据,真实环境需要从数据库查询 return "gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__"; } - - // 根据ClientId和openid返回LoginId - @Override - public Object getLoginId(String clientId, String openid) { - return 10001; - } - - - - /* - * 以上函数为开发时必须重写实现,其余函数可以按需重写 - */ - + // -------------- 其它需要重写的函数 } diff --git a/sa-token-demo/sa-token-demo-oauth2-server/src/main/resources/application.yml b/sa-token-demo/sa-token-demo-oauth2-server/src/main/resources/application.yml index 23244140..6909289f 100644 --- a/sa-token-demo/sa-token-demo-oauth2-server/src/main/resources/application.yml +++ b/sa-token-demo/sa-token-demo-oauth2-server/src/main/resources/application.yml @@ -5,13 +5,14 @@ server: sa-token: # token名称 (同时也是cookie名称) token-name: satoken-server + # OAuth2.0 配置 + oauth2: + is-code: true + is-implicit: true + is-password: true + is-client: true spring: - # 静态文件路径映射 - resources: - static-locations: classpath:/META-INF/resources/,classpath:/resources/, classpath:/static/, classpath:/public/ - # static-locations: file:E:\work\project-yun\sa-token\sa-token-demo-oauth2\sa-token-demo-oauth2-server\src\main\resources\static\ - # redis配置 redis: # Redis数据库索引(默认为0) diff --git a/sa-token-demo/sa-token-demo-oauth2-server/src/main/resources/templates/confirm.html b/sa-token-demo/sa-token-demo-oauth2-server/src/main/resources/templates/confirm.html new file mode 100644 index 00000000..7ede1714 --- /dev/null +++ b/sa-token-demo/sa-token-demo-oauth2-server/src/main/resources/templates/confirm.html @@ -0,0 +1,103 @@ + + + + + Sa-OAuth2-认证中心-确认授权页 + + + + + + + + + + diff --git a/sa-token-demo/sa-token-demo-oauth2-server/src/main/resources/templates/login.html b/sa-token-demo/sa-token-demo-oauth2-server/src/main/resources/templates/login.html new file mode 100644 index 00000000..5972291f --- /dev/null +++ b/sa-token-demo/sa-token-demo-oauth2-server/src/main/resources/templates/login.html @@ -0,0 +1,54 @@ + + + + + Sa-OAuth2-认证中心-登录页 + + + + + + + + + + diff --git a/sa-token-demo/sa-token-demo-springboot/pom.xml b/sa-token-demo/sa-token-demo-springboot/pom.xml index 10585bd6..259ffcfa 100644 --- a/sa-token-demo/sa-token-demo-springboot/pom.xml +++ b/sa-token-demo/sa-token-demo-springboot/pom.xml @@ -79,7 +79,6 @@ 5.5.4 --> - diff --git a/sa-token-demo/sa-token-demo-webflux/src/main/java/com/pj/SaTokenWebfluxDemoApplication.java b/sa-token-demo/sa-token-demo-webflux/src/main/java/com/pj/SaTokenWebfluxDemoApplication.java index 783c0e8e..330a23ec 100644 --- a/sa-token-demo/sa-token-demo-webflux/src/main/java/com/pj/SaTokenWebfluxDemoApplication.java +++ b/sa-token-demo/sa-token-demo-webflux/src/main/java/com/pj/SaTokenWebfluxDemoApplication.java @@ -1,16 +1,5 @@ package com.pj; -//import org.springframework.boot.SpringApplication; -//import org.springframework.boot.autoconfigure.SpringBootApplication; -//import org.springframework.context.annotation.Bean; -//import org.springframework.http.MediaType; -//import org.springframework.web.reactive.function.server.RequestPredicates; -//import org.springframework.web.reactive.function.server.RouterFunction; -//import org.springframework.web.reactive.function.server.RouterFunctions; -//import org.springframework.web.reactive.function.server.ServerRequest; -//import org.springframework.web.reactive.function.server.ServerResponse; - - import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/sa-token-doc/doc/more/tj-gzh.md b/sa-token-doc/doc/more/tj-gzh.md index f5a55801..f2f83656 100644 --- a/sa-token-doc/doc/more/tj-gzh.md +++ b/sa-token-doc/doc/more/tj-gzh.md @@ -7,6 +7,36 @@ + + + + + + + + + + - - -
+ + 终码一生 + 分享Java开发技术(JVM,多线程,高并发,性能调优) + + + CodeSheep + 一只爱技术的程序羊,想把分享变成一种习惯! + ---
+ + Java开发宝典 + 分享Java基础、Java框架、数据库、微服务、中间件、分布式、架构等技术干货 + + + MarkerHub + 专注于梳理java知识,解析开源项目。 + + + 架构师必备 + 分享干货文章,做一个有逼格的架构师社区! + Github导航站 @@ -17,9 +47,6 @@ Java爱好者 分享Java开发编程资源和Java技术文章 ---
diff --git a/sa-token-doc/doc/plugin/alone-redis.md b/sa-token-doc/doc/plugin/alone-redis.md index d2d88ef5..9dd817c3 100644 --- a/sa-token-doc/doc/plugin/alone-redis.md +++ b/sa-token-doc/doc/plugin/alone-redis.md @@ -45,19 +45,23 @@ sa-token: password: # 连接超时时间(毫秒) timeout: 10ms - lettuce: - pool: - # 连接池最大连接数 - max-active: 200 - # 连接池最大阻塞等待时间(使用负值表示没有限制) - max-wait: -1ms - # 连接池中的最大空闲连接 - max-idle: 10 - # 连接池中的最小空闲连接 - min-idle: 0 + +spring: + # 配置业务使用的Redis连接 + redis: + # Redis数据库索引(默认为0) + database: 0 + # Redis服务器地址 + host: 127.0.0.1 + # Redis服务器连接端口 + port: 6379 + # Redis服务器连接密码(默认为空) + password: + # 连接超时时间(毫秒) + timeout: 10ms ``` -具体可参考:[码云:application.yml](https://gitee.com/dromara/sa-token/blob/dev/sa-token-demo/sa-token-demo-alone-redis/src/main/resources/application.yml) +具体可参考示例:[码云:application.yml](https://gitee.com/dromara/sa-token/blob/dev/sa-token-demo/sa-token-demo-alone-redis/src/main/resources/application.yml) ### 3、测试 diff --git a/sa-token-doc/doc/use/dao-extend.md b/sa-token-doc/doc/use/dao-extend.md index f66e27a0..18c8097d 100644 --- a/sa-token-doc/doc/use/dao-extend.md +++ b/sa-token-doc/doc/use/dao-extend.md @@ -84,5 +84,4 @@ spring: 更多框架的集成方案正在更新中... (欢迎大家提交pr) -?> SerializationException序列化异常, 如果你在集成redis的时候遇到这个报错不要紧张, 下面说一下可能的原因 -?> 当你第一次使用redis的时候默认是可以的运行的, 但是切换到另一种序列化方式后就报错了, 这是因为默认SaToken会直接读取之前存储的数据, 以jackson方式读取jdk默认序列化的内容或者反过来读取都会导致读取失败. 此时只需要删除与SaToken相关的redis数据就好了 + diff --git a/sa-token-doc/doc/use/kick.md b/sa-token-doc/doc/use/kick.md index ac265373..db0b611e 100644 --- a/sa-token-doc/doc/use/kick.md +++ b/sa-token-doc/doc/use/kick.md @@ -51,6 +51,6 @@ StpUtil.untieDisable(10001); // 先踢下线 StpUtil.logoutByLoginId(10001); // 再封禁账号 -StpUtil.disable(10001, 86400); +StpUtil.disableLoginId(10001, 86400); ``` diff --git a/sa-token-doc/doc/use/login-auth.md b/sa-token-doc/doc/use/login-auth.md index e002a165..8e8a9f43 100644 --- a/sa-token-doc/doc/use/login-auth.md +++ b/sa-token-doc/doc/use/login-auth.md @@ -67,7 +67,4 @@ StpUtil.getTokenInfo(); ?> 有关TokenInfo参数详解,请参考:[参考:TokenInfo参数详解](/fun/token-info) -?> Stpuitl依赖的是当前的线程上下文,不支持多线程异步调用 -?> 开发者传到其它的异步方法里面,需要先将必要参数取出来,否则这一块的数据会丢失的 - diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/SaOAuth2Manager.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/SaOAuth2Manager.java index 8c153d21..2ad2854b 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/SaOAuth2Manager.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/SaOAuth2Manager.java @@ -3,7 +3,7 @@ package cn.dev33.satoken.oauth2; import cn.dev33.satoken.oauth2.config.SaOAuth2Config; /** - * sa-token oauth2 模块 总控类 + * Sa-Token-OAuth2 模块 总控类 * * @author kong * diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2Config.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2Config.java index de245b7c..06ecd72a 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2Config.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/config/SaOAuth2Config.java @@ -1,40 +1,118 @@ package cn.dev33.satoken.oauth2.config; +import java.io.Serializable; import java.util.function.BiFunction; -import java.util.function.Function; import java.util.function.Supplier; -import cn.dev33.satoken.exception.SaTokenException; import cn.dev33.satoken.util.SaResult; /** - * sa-token oauth2 配置类 Model + * Sa-Token-OAuth2 配置类 Model * @author kong * */ -public class SaOAuth2Config { +public class SaOAuth2Config implements Serializable { - /** - * 授权码默认保存的时间(单位秒) 默认五分钟 - */ - private long codeTimeout = 60 * 5; + private static final long serialVersionUID = -6541180061782004705L; - /** - * access_token默认保存的时间(单位秒) 默认两个小时 - */ - private long accessTokenTimeout = 60 * 60 * 2; + /** 是否打开模式:授权码(Authorization Code) */ + public Boolean isCode = true; - /** - * refresh_token默认保存的时间(单位秒) 默认30 天 - */ - private long refreshTokenTimeout = 60 * 60 * 24 * 30; + /** 是否打开模式:隐藏式(Implicit) */ + public Boolean isImplicit = false; - /** - * client_token默认保存的时间(单位秒) 默认两个小时 - */ - private long clientTokenTimeout = 60 * 60 * 2; + /** 是否打开模式:密码式(Password) */ + public Boolean isPassword = false; + /** 是否打开模式:凭证式(Client Credentials) */ + public Boolean isClient = false; + + /** 是否在每次 Refresh-Token 刷新 Access-Token 时,产生一个新的 Refresh-Token */ + public Boolean isNewRefresh = false; + /** Code授权码 保存的时间(单位秒) 默认五分钟 */ + public long codeTimeout = 60 * 5; + + /** Access-Token 保存的时间(单位秒) 默认两个小时 */ + public long accessTokenTimeout = 60 * 60 * 2; + + /** Refresh-Token 保存的时间(单位秒) 默认30 天 */ + public long refreshTokenTimeout = 60 * 60 * 24 * 30; + + /** Client-Token 保存的时间(单位秒) 默认两个小时 */ + public long clientTokenTimeout = 60 * 60 * 2; + + + /** + * @return isCode + */ + public Boolean getIsCode() { + return isCode; + } + + /** + * @param isCode 要设置的 isCode + */ + public void setIsCode(Boolean isCode) { + this.isCode = isCode; + } + + /** + * @return isImplicit + */ + public Boolean getIsImplicit() { + return isImplicit; + } + + /** + * @param isImplicit 要设置的 isImplicit + */ + public void setIsImplicit(Boolean isImplicit) { + this.isImplicit = isImplicit; + } + + /** + * @return isPassword + */ + public Boolean getIsPassword() { + return isPassword; + } + + /** + * @param isPassword 要设置的 isPassword + */ + public void setIsPassword(Boolean isPassword) { + this.isPassword = isPassword; + } + + /** + * @return isClient + */ + public Boolean getIsClient() { + return isClient; + } + + /** + * @param isClient 要设置的 isClient + */ + public void setIsClient(Boolean isClient) { + this.isClient = isClient; + } + + /** + * @return isNewRefresh + */ + public Boolean getIsNewRefresh() { + return isNewRefresh; + } + + /** + * @param isNewRefresh 要设置的 isNewRefresh + */ + public void setIsNewRefresh(Boolean isNewRefresh) { + this.isNewRefresh = isNewRefresh; + } + /** * @return codeTimeout */ @@ -100,26 +178,13 @@ public class SaOAuth2Config { } - - // -------------------- SaOAuth2Handle 所有回调函数 -------------------- - /** * OAuth-Server端:未登录时返回的View */ public Supplier notLoginView = () -> "当前会话在OAuth-Server认证中心尚未登录"; - /** - * OAuth-Server端:重定向URL无效时返回的View - */ - public BiFunction invalidUrlView = (clientId, url) -> "无效重定向URL:" + url; - - /** - * OAuth-Server端:Client请求的Scope暂未签约时返回的View - */ - public BiFunction invalidScopeView = (clientId, scope) -> "请求的Scope暂未签约"; - /** * OAuth-Server端:确认授权时返回的View */ @@ -130,12 +195,6 @@ public class SaOAuth2Config { */ public BiFunction doLoginHandle = (name, pwd) -> SaResult.error(); - /** - * SSO-Client端:发送Http请求的处理函数 - */ - public Function sendHttp = url -> {throw new SaTokenException("请配置Http处理器");}; - - /** * @param notLoginView OAuth-Server端:未登录时返回的View * @return 对象自身 @@ -145,24 +204,6 @@ public class SaOAuth2Config { return this; } - /** - * @param invalidScopeView OAuth-Server端:重定向URL无效时返回的View - * @return 对象自身 - */ - public SaOAuth2Config setInvalidUrlView(BiFunction invalidUrlView) { - this.invalidUrlView = invalidUrlView; - return this; - } - - /** - * @param invalidScopeView OAuth-Server端:Client请求的Scope暂未签约时返回的View - * @return 对象自身 - */ - public SaOAuth2Config setInvalidScopeView(BiFunction invalidScopeView) { - this.invalidScopeView = invalidScopeView; - return this; - } - /** * @param confirmView OAuth-Server端:确认授权时返回的View * @return 对象自身 @@ -181,23 +222,13 @@ public class SaOAuth2Config { return this; } - /** - * @param sendHttp 发送Http请求的处理函数 - * @return 对象自身 - */ - public SaOAuth2Config setSendHttp(Function sendHttp) { - this.sendHttp = sendHttp; - return this; - } - - - - @Override public String toString() { - return "SaOAuth2Config [codeTimeout=" + codeTimeout + ", accessTokenTimeout=" + accessTokenTimeout - + ", refreshTokenTimeout=" + refreshTokenTimeout + "]"; + return "SaOAuth2Config [isCode=" + isCode + ", isImplicit=" + isImplicit + ", isPassword=" + isPassword + + ", isClient=" + isClient + ", isNewRefresh=" + isNewRefresh + ", codeTimeout=" + codeTimeout + + ", accessTokenTimeout=" + accessTokenTimeout + ", refreshTokenTimeout=" + refreshTokenTimeout + + ", clientTokenTimeout=" + clientTokenTimeout + "]"; } - + } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Consts.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Consts.java index 5848513f..f84082c6 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Consts.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Consts.java @@ -12,10 +12,12 @@ public class SaOAuth2Consts { * @author kong */ public static final class Api { - - /** OAuth-Server端:授权地址 */ public static String authorize = "/oauth2/authorize"; - + public static String token = "/oauth2/token"; + public static String refresh = "/oauth2/refresh"; + public static String client_token = "/oauth2/client_token"; + public static String doLogin = "/oauth2/doLogin"; + public static String doConfirm = "/oauth2/doConfirm"; } /** @@ -23,70 +25,44 @@ public class SaOAuth2Consts { * @author kong */ public static final class Param { - - /** authorize 的 返回值类型 */ public static String response_type = "response_type"; - - /** client_id 参数名称 */ public static String client_id = "client_id"; - - /** client_secret 参数名称 */ public static String client_secret = "client_secret"; - - /** redirect_uri 参数名称 */ public static String redirect_uri = "redirect_uri"; - - /** scope 参数名称 */ public static String scope = "scope"; - - /** state */ public static String state = "state"; - - /** code 参数名称 */ public static String code = "code"; - - /** token 参数名称 */ public static String token = "token"; - - /** grant_type 参数名称 */ + public static String refresh_token = "refresh_token"; public static String grant_type = "grant_type"; - + public static String username = "username"; + public static String password = "password"; + public static String name = "name"; + public static String pwd = "pwd"; + } + + /** + * 所有返回类型 + */ + public static final class ResponseType { + public static String code = "code"; + public static String token = "token"; } /** * 所有授权类型 */ - public static final class AuthType { - - /** 方式一:授权码 */ - public static String code = "code"; - - /** 方式二:隐藏式 */ - public static String token = "token"; - - /** 方式三:密码式 */ - public static String password = "password"; - - /** 方式四:凭证式 */ - public static String client_credentials = "client_credentials"; - + public static final class GrantType { public static String authorization_code = "authorization_code"; - + public static String refresh_token = "refresh_token"; + public static String password = "password"; + public static String client_credentials = "client_credentials"; } - - - /** - * 在保存授权码时用到的key - */ - public static final String UNLIMITED_DOMAIN = "*"; - - /** 表示OK的返回结果 */ public static final String OK = "ok"; - /** 表示请求没有得到任何有效处理 */ - public static final String NOT_HANDLE = "not handle"; - + /** 表示请求没有得到任何有效处理 {msg: "not handle"} */ + public static final String NOT_HANDLE = "{\"msg\": \"not handle\"}"; } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Handle.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Handle.java index 6e915100..b3a15106 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Handle.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Handle.java @@ -5,12 +5,15 @@ import cn.dev33.satoken.context.model.SaRequest; import cn.dev33.satoken.context.model.SaResponse; import cn.dev33.satoken.oauth2.SaOAuth2Manager; import cn.dev33.satoken.oauth2.config.SaOAuth2Config; -import cn.dev33.satoken.oauth2.logic.SaOAuth2Consts.AuthType; +import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception; +import cn.dev33.satoken.oauth2.logic.SaOAuth2Consts.Api; +import cn.dev33.satoken.oauth2.logic.SaOAuth2Consts.GrantType; import cn.dev33.satoken.oauth2.logic.SaOAuth2Consts.Param; +import cn.dev33.satoken.oauth2.logic.SaOAuth2Consts.ResponseType; import cn.dev33.satoken.oauth2.model.AccessTokenModel; +import cn.dev33.satoken.oauth2.model.ClientTokenModel; import cn.dev33.satoken.oauth2.model.CodeModel; import cn.dev33.satoken.oauth2.model.RequestAuthModel; -import cn.dev33.satoken.router.SaRouter; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaResult; @@ -20,106 +23,236 @@ import cn.dev33.satoken.util.SaResult; * */ public class SaOAuth2Handle { - + /** - * 处理Server端请求 + * 处理Server端请求, 路由分发 * @return 处理结果 */ - public static Object authorize() { + public static Object serverRequest() { // 获取变量 SaRequest req = SaHolder.getRequest(); SaResponse res = SaHolder.getResponse(); SaOAuth2Config cfg = SaOAuth2Manager.getConfig(); - // StpLogic stpLogic = SaSsoUtil.saSsoTemplate.stpLogic; - // match(Api.authorize) && - // 授权 - if(req.isParam(Param.response_type, AuthType.code)) { - // 1、构建请求Model TODO: 貌似这个RequestAuthModel对象也可以省略掉 - RequestAuthModel ra = SaOAuth2Util.generateRequestAuth(req, StpUtil.getLoginId()); - - // 2、如果尚未登录, 则先去登录 - if(StpUtil.isLogin() == false) { - return cfg.notLoginView.get(); - } - - // 3、判断:重定向域名的格式是否合法 - boolean isRigh = SaOAuth2Util.isRightUrl(ra.clientId, ra.redirectUri); - if(isRigh == false) { - return cfg.invalidUrlView.apply(ra.clientId, ra.redirectUri); - } - - // 4、判断:此次申请的Scope,该Client是否已经签约 - boolean isContract = SaOAuth2Util.isContract(ra.clientId, ra.scope); - if(isContract == false) { - return cfg.invalidScopeView.apply(ra.clientId, ra.scope); - } - - // 5、判断:此次申请的Scope,该用户是否已经授权过了 - boolean isGrant = SaOAuth2Util.isGrant(StpUtil.getLoginId(), ra.clientId, ra.scope); - if(isGrant == false) { - // 如果尚未授权,则转到授权页面,开始授权操作 - return cfg.confirmView.apply(ra.clientId, ra.scope); - } + // ------------------ 路由分发 ------------------ + + // 模式一:Code授权码 + if(req.isPath(Api.authorize) && req.isParam(Param.response_type, ResponseType.code) && cfg.isCode) { + return authorize(req, res, cfg); + } - // 6、开始重定向授权,下放code + // Code授权码 获取 Access-Token + if(req.isPath(Api.token) && req.isParam(Param.grant_type, GrantType.authorization_code)) { + return token(req, res, cfg); + } + + // Refresh-Token 刷新 Access-Token + if(req.isPath(Api.refresh) && req.isParam(Param.grant_type, GrantType.refresh_token)) { + return refreshToken(req); + } + + // doLogin 登录接口 + if(req.isPath(Api.doLogin)) { + return doLogin(req, res, cfg); + } + + // doConfirm 确认授权接口 + if(req.isPath(Api.doConfirm)) { + return doConfirm(req); + } + + // 模式二:隐藏式 + if(req.isPath(Api.authorize) && req.isParam(Param.response_type, ResponseType.token) && cfg.isImplicit) { + return authorize(req, res, cfg); + } + + // 模式三:密码式 + if(req.isPath(Api.token) && req.isParam(Param.grant_type, GrantType.password) && cfg.isPassword) { + return password(req, res, cfg); + } + + // 模式四:凭证式 + if(req.isPath(Api.client_token) && req.isParam(Param.grant_type, GrantType.client_credentials) && cfg.isClient) { + return clientToken(req, res, cfg); + } + + // 默认返回 + return SaOAuth2Consts.NOT_HANDLE; + } + + /** + * 模式一:Code授权码 / 模式二:隐藏式 + * @param req 请求对象 + * @param res 响应对象 + * @param cfg 配置对象 + * @return 处理结果 + */ + public static Object authorize(SaRequest req, SaResponse res, SaOAuth2Config cfg) { + + // 1、如果尚未登录, 则先去登录 + if(StpUtil.isLogin() == false) { + return cfg.notLoginView.get(); + } + + // 2、构建请求Model + RequestAuthModel ra = SaOAuth2Util.generateRequestAuth(req, StpUtil.getLoginId()); + + // 3、校验:重定向域名是否合法 + SaOAuth2Util.checkRightUrl(ra.clientId, ra.redirectUri); + + // 4、校验:此次申请的Scope,该Client是否已经签约 + SaOAuth2Util.checkContract(ra.clientId, ra.scope); + + // 5、判断:如果此次申请的Scope,该用户尚未授权,则转到授权页面 + boolean isGrant = SaOAuth2Util.isGrant(ra.loginId, ra.clientId, ra.scope); + if(isGrant == false) { + return cfg.confirmView.apply(ra.clientId, ra.scope); + } + + // 6、判断授权类型 + // 如果是 授权码式,则:开始重定向授权,下放code + if(ResponseType.code.equals(ra.responseType)) { CodeModel codeModel = SaOAuth2Util.generateCode(ra); String redirectUri = SaOAuth2Util.buildRedirectUri(ra.redirectUri, codeModel.code, ra.state); return res.redirect(redirectUri); } - - - - - // 默认返回 - return SaOAuth2Consts.NOT_HANDLE; - } - - /** - * 获取Token - * @return - */ - public static Object token() { - - // 获取变量 - SaRequest req = SaHolder.getRequest(); - // SaResponse res = SaHolder.getResponse(); - // SaOAuth2Config cfg = SaOAuth2Manager.getConfig(); - - // 根据code换token - if(req.isParam(Param.grant_type, AuthType.authorization_code)) { - System.out.println("------------获取token,,,"); - - // 获取参数 - String code = req.getParamNotNull(Param.code); // code码 - String clientId = req.getParamNotNull(Param.client_id); // 应用id - String clientSecret = req.getParamNotNull(Param.client_secret); // 应用秘钥 - String redirectUri = req.getParam(Param.redirect_uri); // 应用秘钥 - - // 校验参数 - SaOAuth2Util.checkCodeIdSecret(code, clientId, clientSecret, redirectUri); - - // 构建 access_token - AccessTokenModel token = SaOAuth2Util.generateAccessToken(code); - - // 返回 - return SaResult.data(token.toLineMap()); + // 如果是 隐藏式,则:开始重定向授权,下放 token + if(ResponseType.token.equals(ra.responseType)) { + AccessTokenModel at = SaOAuth2Util.generateAccessToken(ra, false); + String redirectUri = SaOAuth2Util.buildImplicitRedirectUri(ra.redirectUri, at.accessToken, ra.state); + return res.redirect(redirectUri); } // 默认返回 - return SaOAuth2Consts.NOT_HANDLE; + throw new SaOAuth2Exception("无效response_type: " + ra.responseType); } - - + /** + * Code授权码 获取 Access-Token + * @param req 请求对象 + * @param res 响应对象 + * @param cfg 配置对象 + * @return 处理结果 + */ + public static Object token(SaRequest req, SaResponse res, SaOAuth2Config cfg) { + // 获取参数 + String code = req.getParamNotNull(Param.code); + String clientId = req.getParamNotNull(Param.client_id); + String clientSecret = req.getParamNotNull(Param.client_secret); + String redirectUri = req.getParam(Param.redirect_uri); + + // 校验参数 + SaOAuth2Util.checkGainTokenParam(code, clientId, clientSecret, redirectUri); + + // 构建 Access-Token + AccessTokenModel token = SaOAuth2Util.generateAccessToken(code); + + // 返回 + return SaResult.data(token.toLineMap()); + } /** - * 路由匹配算法 - * @param pattern 路由表达式 - * @return 是否可以匹配 + * Refresh-Token 刷新 Access-Token + * @param req 请求对象 + * @return 处理结果 */ - static boolean match(String pattern) { - return SaRouter.isMatch(pattern, SaHolder.getRequest().getRequestPath()); + public static Object refreshToken(SaRequest req) { + // 获取参数 + String clientId = req.getParamNotNull(Param.client_id); + String clientSecret = req.getParamNotNull(Param.client_secret); + String refreshToken = req.getParamNotNull(Param.refresh_token); + + // 校验参数 + SaOAuth2Util.checkRefreshTokenParam(clientId, clientSecret, refreshToken); + + // 获取新Token返回 + Object data = SaOAuth2Util.saOAuth2Template.refreshAccessToken(refreshToken).toLineMap(); + return SaResult.data(data); } + + /** + * doLogin 登录接口 + * @param req 请求对象 + * @param res 响应对象 + * @param cfg 配置对象 + * @return 处理结果 + */ + public static Object doLogin(SaRequest req, SaResponse res, SaOAuth2Config cfg) { + return cfg.doLoginHandle.apply(req.getParamNotNull(Param.name), req.getParamNotNull("pwd")); + } + + /** + * doConfirm 确认授权接口 + * @param req 请求对象 + * @return 处理结果 + */ + public static Object doConfirm(SaRequest req) { + String clientId = req.getParamNotNull(Param.client_id); + String scope = req.getParamNotNull(Param.scope); + Object loginId = StpUtil.getLoginId(); + SaOAuth2Util.saveGrantScope(clientId, loginId, scope); + return SaResult.ok(); + } + + /** + * 模式三:密码式 + * @param req 请求对象 + * @param res 响应对象 + * @param cfg 配置对象 + * @return 处理结果 + */ + public static Object password(SaRequest req, SaResponse res, SaOAuth2Config cfg) { + + // 1、获取请求参数 + String username = req.getParamNotNull(Param.username); + String password = req.getParamNotNull(Param.password); + String clientId = req.getParamNotNull(Param.client_id); + + // 2、校验client_id + SaOAuth2Util.checkClientModel(clientId); + + // 3、防止因前端误传token造成逻辑干扰 + SaHolder.getStorage().set(StpUtil.stpLogic.splicingKeyJustCreatedSave(), "no-token"); + + // 4、调用API 开始登录,如果没能成功登录,则直接退出 + Object retObj = cfg.doLoginHandle.apply(username, password); + if(StpUtil.isLogin() == false) { + return retObj; + } + + // 5、构建 ra对象 + RequestAuthModel ra = new RequestAuthModel(); + ra.clientId = clientId; + ra.loginId = StpUtil.getLoginId(); + ra.scope = req.getParam(Param.scope, ""); + + // 6、生成 Access-Token + AccessTokenModel at = SaOAuth2Util.generateAccessToken(ra, true); + + // 7、返回 Access-Token + return SaResult.data(at.toLineMap()); + } + + /** + * 模式四:凭证式 + * @return 处理结果 + */ + public static Object clientToken(SaRequest req, SaResponse res, SaOAuth2Config cfg) { + + // 获取参数 + String clientId = req.getParamNotNull(Param.client_id); + String clientSecret = req.getParamNotNull(Param.client_secret); + String scope = req.getParam(Param.scope); + + // 校验 ClientSecret + SaOAuth2Util.checkClientSecret(clientId, clientSecret); + + // 返回 Client-Token + ClientTokenModel ct = SaOAuth2Util.generateClientToken(clientId, scope); + + // 返回 Client-Token + return SaResult.data(ct.toLineMap()); + } + } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Template.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Template.java index e01251da..ed97b8a2 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Template.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Template.java @@ -1,12 +1,9 @@ package cn.dev33.satoken.oauth2.logic; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import cn.dev33.satoken.SaManager; import cn.dev33.satoken.context.model.SaRequest; -import cn.dev33.satoken.exception.SaTokenException; import cn.dev33.satoken.oauth2.SaOAuth2Manager; import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception; import cn.dev33.satoken.oauth2.logic.SaOAuth2Consts.Param; @@ -15,65 +12,27 @@ import cn.dev33.satoken.oauth2.model.ClientTokenModel; import cn.dev33.satoken.oauth2.model.CodeModel; import cn.dev33.satoken.oauth2.model.RefreshTokenModel; import cn.dev33.satoken.oauth2.model.RequestAuthModel; +import cn.dev33.satoken.oauth2.model.SaClientModel; import cn.dev33.satoken.util.SaFoxUtil; /** - * sa-token-oauth2 模块 逻辑接口 + * Sa-Token-OAuth2 模块 代码实现 * @author kong * */ public class SaOAuth2Template { - // ------------------- 获取数据 + // ------------------- 获取数据 (开发者必须重写的函数) /** - * 返回此平台所有权限集合 - * @return 此平台所有权限名称集合 + * 根据id获取Client信息 + * @param clientId 应用id + * @return ClientModel */ - public List getAppScopeList() { - return Arrays.asList("userinfo"); - } - - /** - * 返回指定Client签约的所有Scope名称集合 - * @param clientId 应用id - * @return Scope集合 - */ - public List getClientScopeList(String clientId) { - // 默认返回此APP的所有权限 - return getAppScopeList(); - } - - /** - * [转Redis]获取指定 LoginId 对指定 Client 已经授权过的所有 Scope - * @param clientId 应用id - * @param loginId 账号id - * @return Scope集合 - */ - public List getGrantScopeList(Object loginId, String clientId) { - // 默认返回空集合 - return Arrays.asList(); - } - - /** - * 返回指定Client允许的回调域名, 多个用逗号隔开, *代表不限制 - * @param clientId 应用id - * @return domain集合 - */ - public String getClientDomain(String clientId) { - return "*"; - } - - /** - * 返回指定ClientId的ClientSecret - * @param clientId 应用id - * @return 此应用的秘钥 - */ - public String getClientSecret(String clientId) { + public SaClientModel getClientModel(String clientId) { return null; } - /** - * 根据ClientId和LoginId返回openid + * 根据ClientId 和 LoginId 获取openid * @param clientId 应用id * @param loginId 账号id * @return 此账号在此Client下的openid @@ -81,86 +40,52 @@ public class SaOAuth2Template { public String getOpenid(String clientId, Object loginId) { return null; } - + + // ------------------- 资源获取 /** - * [可取消]根据ClientId和openid返回LoginId - * @param clientId 应用id - * @param openid openid + * 根据id获取Client信息, 如果Client为空,则抛出异常 + * @param clientId 应用id + * @return ClientModel + */ + public SaClientModel checkClientModel(String clientId) { + SaClientModel clientModel = getClientModel(clientId); + if(clientModel == null) { + throw new SaOAuth2Exception("无效client_id: " + clientId); + } + return clientModel; + } + /** + * 获取 access_token 所代表的LoginId + * @param accessToken access_token * @return LoginId */ - public Object getLoginId(String clientId, String openid) { - return null; + public Object getLoginIdByAccessToken(String accessToken) { + return checkAccessToken(accessToken).loginId; } - - // ------------------- conver 数据转换 /** - * [OK] 将 CodeModel 转换为 AccessTokenModel - * @param cm CodeModel对象 - * @return AccessToken对象 + * 获取 Access-Token,如果AccessToken为空则抛出异常 + * @param accessToken . + * @return . */ - public AccessTokenModel converCodeToAccessToken(CodeModel cm) { - AccessTokenModel at = new AccessTokenModel(); - at.accessToken = randomAccessToken(cm.clientId, cm.loginId, cm.scope); - at.refreshToken = randomRefreshToken(cm.clientId, cm.loginId, cm.scope); - at.clientId = cm.clientId; - at.loginId = cm.loginId; - at.scope = cm.scope; - at.openid = getOpenid(cm.clientId, cm.loginId); - at.expiresTime = System.currentTimeMillis() + (SaOAuth2Manager.getConfig().getAccessTokenTimeout() * 1000); - // at.refreshExpiresTime = System.currentTimeMillis() + (SaOAuth2Manager.getConfig().getRefreshTokenTimeout() * 1000); + public AccessTokenModel checkAccessToken(String accessToken) { + AccessTokenModel at = getAccessToken(accessToken); + SaOAuth2Exception.throwBy(at == null, "无效:access_token" + accessToken); return at; } /** - * [OK] 将 AccessToken 转换为 RefreshTokenModel - * @param at AccessToken对象 - * @return RefreshToken对象 + * 获取 Client-Token,如果ClientToken为空则抛出异常 + * @param clientToken . + * @return . */ - public RefreshTokenModel converAccessTokenToRefreshToken(AccessTokenModel at) { - RefreshTokenModel rt = new RefreshTokenModel(); - rt.refreshToken = at.refreshToken; - rt.clientId = at.clientId; - rt.loginId = at.loginId; - rt.scope = at.scope; - rt.openid = at.openid; - rt.expiresTime = System.currentTimeMillis() + (SaOAuth2Manager.getConfig().getRefreshTokenTimeout() * 1000); - return rt; - } - /** - * [OK] 将 RefreshTokenModel 转换为 AccessTokenModel - * @param codeModel CodeModel对象 - * @return RefreshToken对象 - */ - public AccessTokenModel converRefreshTokenToAccessToken(RefreshTokenModel rt) { - AccessTokenModel at = new AccessTokenModel(); - at.accessToken = randomAccessToken(rt.clientId, rt.loginId, rt.scope); - at.refreshToken = rt.refreshToken; - at.clientId = rt.clientId; - at.loginId = rt.loginId; - at.scope = rt.scope; - at.openid = rt.openid; - at.expiresTime = System.currentTimeMillis() + (SaOAuth2Manager.getConfig().getAccessTokenTimeout() * 1000); - at.refreshExpiresTime = rt.expiresTime; - return at; - } - /** - * 将指定字符串按照逗号分隔符转化为字符串集合 - * @param str 字符串 - * @return 分割后的字符串集合 - */ - public List convertStringToList(String str) { - String[] arr = str.split(","); - List list = new ArrayList(); - for (String s : arr) { - if(SaFoxUtil.isEmpty(s) == false) { - list.add(s); - } - } - return list; + public ClientTokenModel checkClientToken(String clientToken) { + ClientTokenModel ct = getClientToken(clientToken); + SaOAuth2Exception.throwBy(ct == null, "无效:client_token" + ct); + return ct; } // ------------------- generate 构建数据 /** - * 构建:请求Model + * 构建Model:请求Model * @param req SaRequest对象 * @param loginId 账号id * @return RequestAuthModel对象 @@ -176,134 +101,142 @@ public class SaOAuth2Template { return ra; } /** - * 构建:授权码Model - * @param ra 请求授权参数Model + * 构建Model:Code授权码 + * @param ra 请求参数Model * @return 授权码Model */ public CodeModel generateCode(RequestAuthModel ra) { // 删除旧Code - String oldCode = getCodeValue(ra.clientId, ra.loginId); - if(oldCode != null) { - deleteCode(oldCode); - // deleteCodeIndex(ra.clientId, ra.loginId); // 此处无需删除,因为下边的set会直接覆盖掉 - } + deleteCode(getCodeValue(ra.clientId, ra.loginId)); // 生成新Code String code = randomCode(ra.clientId, ra.loginId, ra.scope); - CodeModel codeModel = new CodeModel(code, ra.clientId, ra.scope, ra.loginId, ra.redirectUri); + CodeModel cm = new CodeModel(code, ra.clientId, ra.scope, ra.loginId, ra.redirectUri); // 保存新Code - saveCode(codeModel); - saveCodeIndex(codeModel); + saveCode(cm); + saveCodeIndex(cm); // 返回 - return codeModel; + return cm; } /** - * 构建:AccessToken Model - * @param codeModel 授权码Model - * @return AccessTokenModel + * 构建Model:Access-Token + * @param code 授权码Model + * @return AccessToken Model */ public AccessTokenModel generateAccessToken(String code) { - // 先校验 + // 1、先校验 CodeModel cm = getCode(code); SaOAuth2Exception.throwBy(cm == null, "无效code"); - // 生成token + // 2、删除旧Token + deleteAccessToken(getAccessTokenValue(cm.clientId, cm.loginId)); + deleteRefreshToken(getRefreshTokenValue(cm.clientId, cm.loginId)); + + // 3、生成token AccessTokenModel at = converCodeToAccessToken(cm); RefreshTokenModel rt = converAccessTokenToRefreshToken(at); + at.refreshToken = rt.refreshToken; at.refreshExpiresTime = rt.expiresTime; - // 保存Token + // 4、保存token saveAccessToken(at); saveAccessTokenIndex(at); saveRefreshToken(rt); saveRefreshTokenIndex(rt); - // 删除此Code + // 5、删除此Code deleteCode(code); deleteCodeIndex(cm.clientId, cm.loginId); - // 返回 + // 6、返回 Access-Token return at; } /** - * 刷新:根据 RefreshToken 生成一个 AccessToken - * @param refreshToken refresh_token - * @return 新的 access_token + * 刷新Model:根据 Refresh-Token 生成一个新的 Access-Token + * @param refreshToken Refresh-Token值 + * @return 新的 Access-Token */ public AccessTokenModel refreshAccessToken(String refreshToken) { - // 获取RefreshToken信息 + // 获取 Refresh-Token 信息 RefreshTokenModel rt = getRefreshToken(refreshToken); SaOAuth2Exception.throwBy(rt == null, "无效refresh_token: " + refreshToken); - // 删除旧AccessToken - String atValue = getAccessTokenValue(rt.clientId, rt.loginId); - if(atValue != null) { - deleteAccessToken(atValue); - deleteAccessTokenIndex(rt.clientId, rt.loginId); + // 如果配置了[每次刷新产生新的Refresh-Token] + if(SaOAuth2Manager.getConfig().getIsNewRefresh()) { + // 删除旧 Refresh-Token + deleteRefreshToken(rt.refreshToken); + + // 创建并保持新的 Refresh-Token + rt = converRefreshTokenToRefreshToken(rt); + saveRefreshToken(rt); + saveRefreshTokenIndex(rt); } - // 生成新AccessToken + // 删除旧 Access-Token + deleteAccessToken(getAccessTokenValue(rt.clientId, rt.loginId)); + + // 生成新 Access-Token AccessTokenModel at = converRefreshTokenToAccessToken(rt); - // 保存新AccessToken + // 保存新 Access-Token saveAccessToken(at); saveAccessTokenIndex(at); - // 返回新AccessToken + // 返回新 Access-Token return at; } /** - * 构建:AccessToken Model (根据RequestAuthModel) 用于隐藏式 - * @param ra 请求授权参数Model - * @return 授权码Model + * 构建Model:Access-Token (根据RequestAuthModel构建,用于隐藏式 and 密码式) + * @param ra 请求参数Model + * @param isCreateRt 是否生成对应的Refresh-Token + * @return Access-Token Model */ - public AccessTokenModel generateAccessToken(RequestAuthModel ra) { + public AccessTokenModel generateAccessToken(RequestAuthModel ra, boolean isCreateRt) { - // 删除旧AccessToken - String oldAccessToken = getAccessTokenValue(ra.clientId, ra.loginId); - if(oldAccessToken != null) { - deleteAccessToken(oldAccessToken); + // 1、删除 旧Token + deleteAccessToken(getAccessTokenValue(ra.clientId, ra.loginId)); + if(isCreateRt) { + deleteRefreshToken(getRefreshTokenValue(ra.clientId, ra.loginId)); } - // 生成新AccessToken - String atValue = randomAccessToken(ra.clientId, ra.loginId, ra.scope); - AccessTokenModel at = new AccessTokenModel(atValue, ra.clientId, ra.loginId, ra.scope); + // 2、生成 新Access-Token + String newAtValue = randomAccessToken(ra.clientId, ra.loginId, ra.scope); + AccessTokenModel at = new AccessTokenModel(newAtValue, ra.clientId, ra.loginId, ra.scope); at.openid = getOpenid(ra.clientId, ra.loginId); at.expiresTime = System.currentTimeMillis() + (SaOAuth2Manager.getConfig().getAccessTokenTimeout() * 1000); - // 保存新Token + // 3、生成&保存 Refresh-Token + if(isCreateRt) { + RefreshTokenModel rt = converAccessTokenToRefreshToken(at); + saveRefreshToken(rt); + saveRefreshTokenIndex(rt); + } + + // 5、保存 新Access-Token saveAccessToken(at); saveAccessTokenIndex(at); - // 返回新Token + // 6、返回 新Access-Token return at; } - /** - * 构建:ClientToken Model - * @param ra 请求授权参数Model - * @return ClientToken-Model + * 构建Model:Client-Token + * @param clientId 应用id + * @param scope 授权范围 + * @return Client-Token Model */ public ClientTokenModel generateClientToken(String clientId, String scope) { // 1、删掉 Past-Token - String ptValue = getPastTokenValue(clientId); - if(ptValue != null) { - deleteClientToken(ptValue); - } + deleteClientToken(getPastTokenValue(clientId)); - // 2、将Client-Token 标记 Past-Token + // 2、将Client-Token 标记 Past-Token String ctValue = getClientTokenValue(clientId); - if(ctValue != null) { - ClientTokenModel ct = getClientToken(ctValue); - if(ct != null) { - savePastClientTokenIndex(ct); - } - } + savePastTokenIndex(getClientToken(ctValue)); // 3、生成新Token ClientTokenModel ct = new ClientTokenModel(randomClientToken(clientId, scope), clientId, scope); @@ -316,22 +249,8 @@ public class SaOAuth2Template { // 4、返回 return ct; } - - // ------------------- 其它方法 /** - * 获取 access_token 所代表的LoginId - * @param accessToken access_token - * @return LoginId - */ - public Object getLoginIdByAccessToken(String accessToken) { - AccessTokenModel tokenModel = SaOAuth2Util.getAccessToken(accessToken); - if(tokenModel == null) { - throw new SaTokenException("无效access_token"); - } - return getLoginId(tokenModel.clientId, tokenModel.openid); - } - /** - * [OK] 构建URL:下放授权码URL + * 构建URL:下放Code URL (Authorization Code 授权码) * @param redirectUri 下放地址 * @param code code参数 * @param state state参数 @@ -345,57 +264,54 @@ public class SaOAuth2Template { return url; } /** - * [OK] 构建URL:下放Token URL + * 构建URL:下放Access-Token URL (implicit 隐藏式) * @param redirectUri 下放地址 * @param token token * @param state state参数 * @return 构建完毕的URL */ - public String buildRedirectUri2(String redirectUri, String token, String state) { + public String buildImplicitRedirectUri(String redirectUri, String token, String state) { String url = SaFoxUtil.joinSharpParam(redirectUri, Param.token, token); if(SaFoxUtil.isEmpty(state) == false) { - url = SaFoxUtil.joinParam(url, Param.state, state); + url = SaFoxUtil.joinSharpParam(url, Param.state, state); } return url; } - // ------------------- 数据校验 + // ------------------- check 数据校验 /** - * [OK] 判断:该Client是否签约了指定的Scope - * @param clientId 应用id - * @param scope 权限 - */ - public boolean isContract(String clientId, String scope) { - if(SaFoxUtil.isEmpty(scope)) { - return true; - } - List clientScopeList = getClientScopeList(clientId); - List scopelist = Arrays.asList(scope.split(",")); - return clientScopeList.containsAll(scopelist); - } - /** - * [OK] 指定 loginId 是否对一个 Client 授权给了指定 Scope + * 判断:指定 loginId 是否对一个 Client 授权给了指定 Scope * @param loginId 账号id * @param clientId 应用id * @param scope 权限 * @return 是否已经授权 */ public boolean isGrant(Object loginId, String clientId, String scope) { - List grantScopeList = getGrantScopeList(loginId, clientId); - List scopeList = convertStringToList(scope); + List grantScopeList = SaFoxUtil.convertStringToList(getGrantScope(clientId, loginId)); + List scopeList = SaFoxUtil.convertStringToList(scope); return scopeList.size() == 0 || grantScopeList.containsAll(scopeList); } /** - * [OK] 指定Client使用指定url作为回调地址,是否合法 + * 校验:该Client是否签约了指定的Scope + * @param clientId 应用id + * @param scope 权限(多个用逗号隔开) + */ + public void checkContract(String clientId, String scope) { + List clientScopeList = SaFoxUtil.convertStringToList(checkClientModel(clientId).contractScope); + List scopelist = SaFoxUtil.convertStringToList(scope); + if(clientScopeList.containsAll(scopelist) == false) { + throw new SaOAuth2Exception("请求的Scope暂未签约"); + } + } + /** + * 校验:该Client使用指定url作为回调地址,是否合法 * @param clientId 应用id * @param url 指定url - * @return 是否合法 */ - public boolean isRightUrl(String clientId, String url) { - - // 1、是否是一个有效的url + public void checkRightUrl(String clientId, String url) { + // 1、是否是一个有效的url if(SaFoxUtil.isUrl(url) == false) { - return false; + throw new SaOAuth2Exception("无效redirect_url:" + url); } // 2、截取掉?后面的部分 @@ -405,81 +321,142 @@ public class SaOAuth2Template { } // 3、是否在[允许地址列表]之中 - String domain = getClientDomain(clientId); - if(SaFoxUtil.isEmpty(domain)) { - return false; + List allowList = SaFoxUtil.convertStringToList(checkClientModel(clientId).allowUrl); + if(SaManager.getSaTokenAction().hasElement(allowList, url) == false) { + throw new SaOAuth2Exception("非法redirect_url:" + url); } - List authUrlList = Arrays.asList(domain.replaceAll(" ", "").split(",")); - if(SaManager.getSaTokenAction().hasElement(authUrlList, url) == false) { - return false; - } - - // 验证通过 - return true; } /** - * [OK 方法名改一下]校验code、clientId、clientSecret 三者是否正确 + * 校验:clientId 与 clientSecret 是否正确 + * @param clientId 应用id + * @param clientSecret 秘钥 + * @return SaClientModel对象 + */ + public SaClientModel checkClientSecret(String clientId, String clientSecret) { + SaClientModel cm = checkClientModel(clientId); + SaOAuth2Exception.throwBy(cm.clientSecret == null || cm.clientSecret.equals(clientSecret) == false, "无效client_secret: " + clientSecret); + return cm; + } + /** + * 校验:使用 code 获取 token 时提供的参数校验 * @param code 授权码 * @param clientId 应用id * @param clientSecret 秘钥 - * @param redirectUri 秘钥 + * @param redirectUri 重定向地址 * @return CodeModel对象 */ - public CodeModel checkCodeIdSecret(String code, String clientId, String clientSecret, String redirectUri) { + public CodeModel checkGainTokenParam(String code, String clientId, String clientSecret, String redirectUri) { // 校验:Code是否存在 - CodeModel codeModel = getCode(code); - if(codeModel == null) { - throw new SaOAuth2Exception("无效code"); - } + CodeModel cm = getCode(code); + SaOAuth2Exception.throwBy(cm == null, "无效code: " + code); // 校验:ClientId是否一致 - if(codeModel.getClientId().equals(clientId) == false){ - throw new SaOAuth2Exception("无效client_id"); - } + SaOAuth2Exception.throwBy(cm.clientId.equals(clientId) == false, "无效client_id: " + clientId); // 校验:Secret是否正确 - String dbClientSecret = getClientSecret(clientId); - if(dbClientSecret == null || dbClientSecret.equals(clientSecret) == false){ - throw new SaOAuth2Exception("无效client_secret"); - } + String dbSecret = checkClientModel(clientId).clientSecret; + SaOAuth2Exception.throwBy(dbSecret == null || dbSecret.equals(clientSecret) == false, "无效client_secret: " + clientSecret); // 如果提供了redirectUri,则校验其是否与请求Code时提供的一致 if(SaFoxUtil.isEmpty(redirectUri) == false) { - if(redirectUri.equals(codeModel.redirectUri) == false) { - throw new SaOAuth2Exception("无效redirect_uri"); - } + SaOAuth2Exception.throwBy(redirectUri.equals(cm.redirectUri) == false, "无效redirect_uri: " + redirectUri); } - // 返回CodeMdoel - return codeModel; + // 返回CodeMdoel + return cm; } /** - * 校验access_token、clientId、clientSecret 三者是否正确 - * @param accessToken access_token + * 校验:使用 Refresh-Token 刷新 Access-Token 时提供的参数校验 * @param clientId 应用id * @param clientSecret 秘钥 - * @return AccessTokenModel对象 + * @param refreshToken Refresh-Token + * @return CodeModel对象 */ - public AccessTokenModel checkTokenIdSecret(String accessToken, String clientId, String clientSecret) { + public RefreshTokenModel checkRefreshTokenParam(String clientId, String clientSecret, String refreshToken) { - // 获取授权码信息 - AccessTokenModel tokenModel = getAccessToken(accessToken); + // 校验:Refresh-Token是否存在 + RefreshTokenModel rt = getRefreshToken(refreshToken); + SaOAuth2Exception.throwBy(rt == null, "无效refresh_token: " + refreshToken); + + // 校验:ClientId是否一致 + SaOAuth2Exception.throwBy(rt.clientId.equals(clientId) == false, "无效client_id: " + clientId); - // 验证code、client_id、client_secret - if(tokenModel == null) { - throw new SaTokenException("无效access_token"); - } - if(tokenModel.clientId.equals(clientId) == false){ - throw new SaTokenException("无效client_id"); - } - String dbClientSecret = getClientSecret(clientId); - if(dbClientSecret == null || dbClientSecret.equals(clientSecret)){ - throw new SaTokenException("无效client_secret"); - } + // 校验:Secret是否正确 + String dbSecret = checkClientModel(clientId).clientSecret; + SaOAuth2Exception.throwBy(dbSecret == null || dbSecret.equals(clientSecret) == false, "无效client_secret: " + clientSecret); - // 返回AccessTokenModel - return tokenModel; + // 返回Refresh-Token + return rt; + } + + // ------------------- conver 数据转换 + /** + * 将 Code 转换为 Access-Token + * @param cm CodeModel对象 + * @return AccessToken对象 + */ + public AccessTokenModel converCodeToAccessToken(CodeModel cm) { + AccessTokenModel at = new AccessTokenModel(); + at.accessToken = randomAccessToken(cm.clientId, cm.loginId, cm.scope); + // at.refreshToken = randomRefreshToken(cm.clientId, cm.loginId, cm.scope); + at.clientId = cm.clientId; + at.loginId = cm.loginId; + at.scope = cm.scope; + at.openid = getOpenid(cm.clientId, cm.loginId); + at.expiresTime = System.currentTimeMillis() + (SaOAuth2Manager.getConfig().getAccessTokenTimeout() * 1000); + // at.refreshExpiresTime = System.currentTimeMillis() + (SaOAuth2Manager.getConfig().getRefreshTokenTimeout() * 1000); + return at; + } + /** + * 将 Access-Token 转换为 Refresh-Token + * @param at . + * @return . + */ + public RefreshTokenModel converAccessTokenToRefreshToken(AccessTokenModel at) { + RefreshTokenModel rt = new RefreshTokenModel(); + rt.refreshToken = randomRefreshToken(at.clientId, at.loginId, at.scope); + rt.clientId = at.clientId; + rt.loginId = at.loginId; + rt.scope = at.scope; + rt.openid = at.openid; + rt.expiresTime = System.currentTimeMillis() + (SaOAuth2Manager.getConfig().getRefreshTokenTimeout() * 1000); + // 改变at属性 + at.refreshToken = rt.refreshToken; + at.refreshExpiresTime = rt.expiresTime; + return rt; + } + /** + * 将 Refresh-Token 转换为 Access-Token + * @param rt . + * @return . + */ + public AccessTokenModel converRefreshTokenToAccessToken(RefreshTokenModel rt) { + AccessTokenModel at = new AccessTokenModel(); + at.accessToken = randomAccessToken(rt.clientId, rt.loginId, rt.scope); + at.refreshToken = rt.refreshToken; + at.clientId = rt.clientId; + at.loginId = rt.loginId; + at.scope = rt.scope; + at.openid = rt.openid; + at.expiresTime = System.currentTimeMillis() + (SaOAuth2Manager.getConfig().getAccessTokenTimeout() * 1000); + at.refreshExpiresTime = rt.expiresTime; + return at; + } + /** + * 根据 Refresh-Token 创建一个新的 Refresh-Token + * @param rt . + * @return . + */ + public RefreshTokenModel converRefreshTokenToRefreshToken(RefreshTokenModel rt) { + RefreshTokenModel newRt = new RefreshTokenModel(); + newRt.refreshToken = randomRefreshToken(rt.clientId, rt.loginId, rt.scope); + newRt.expiresTime = System.currentTimeMillis() + (SaOAuth2Manager.getConfig().getRefreshTokenTimeout() * 1000); + newRt.clientId = rt.clientId; + newRt.scope = rt.scope; + newRt.loginId = rt.loginId; + newRt.openid = rt.openid; + return newRt; } // ------------------- save 数据 @@ -488,266 +465,344 @@ public class SaOAuth2Template { * @param c . */ public void saveCode(CodeModel c) { - SaManager.getSaTokenDao().setObject(splicingKeySaveCode(c.code), c, SaOAuth2Manager.getConfig().getCodeTimeout()); + if(c == null) { + return; + } + SaManager.getSaTokenDao().setObject(splicingCodeSaveKey(c.code), c, SaOAuth2Manager.getConfig().getCodeTimeout()); } /** * 持久化:Code-索引 * @param c . */ public void saveCodeIndex(CodeModel c) { - SaManager.getSaTokenDao().set(splicingKeyCodeIndex(c.clientId, c.loginId), c.code, SaOAuth2Manager.getConfig().getCodeTimeout()); + if(c == null) { + return; + } + SaManager.getSaTokenDao().set(splicingCodeIndexKey(c.clientId, c.loginId), c.code, SaOAuth2Manager.getConfig().getCodeTimeout()); } /** * 持久化:AccessToken-Model * @param at . */ public void saveAccessToken(AccessTokenModel at) { - SaManager.getSaTokenDao().setObject(splicingKeySaveAccessToken(at.accessToken), at, at.getExpiresIn()); + if(at == null) { + return; + } + SaManager.getSaTokenDao().setObject(splicingAccessTokenSaveKey(at.accessToken), at, at.getExpiresIn()); } /** * 持久化:AccessToken-索引 * @param at . */ public void saveAccessTokenIndex(AccessTokenModel at) { - SaManager.getSaTokenDao().set(splicingKeyAccessTokenIndex(at.clientId, at.loginId), at.accessToken, at.getExpiresIn()); + if(at == null) { + return; + } + SaManager.getSaTokenDao().set(splicingAccessTokenIndexKey(at.clientId, at.loginId), at.accessToken, at.getExpiresIn()); } /** * 持久化:RefreshToken-Model * @param rt . */ public void saveRefreshToken(RefreshTokenModel rt) { - SaManager.getSaTokenDao().setObject(splicingKeySaveRefreshToken(rt.refreshToken), rt, rt.getExpiresIn()); + if(rt == null) { + return; + } + SaManager.getSaTokenDao().setObject(splicingRefreshTokenSaveKey(rt.refreshToken), rt, rt.getExpiresIn()); } /** * 持久化:RefreshToken-索引 * @param rt . */ public void saveRefreshTokenIndex(RefreshTokenModel rt) { - SaManager.getSaTokenDao().set(splicingKeyRefreshTokenIndex(rt.clientId, rt.loginId), rt.refreshToken, rt.getExpiresIn()); + if(rt == null) { + return; + } + SaManager.getSaTokenDao().set(splicingRefreshTokenIndexKey(rt.clientId, rt.loginId), rt.refreshToken, rt.getExpiresIn()); } /** * 持久化:ClientToken-Model - * @param at . + * @param ct . */ public void saveClientToken(ClientTokenModel ct) { - SaManager.getSaTokenDao().setObject(splicingKeySaveClientToken(ct.clientToken), ct, ct.getExpiresIn()); + if(ct == null) { + return; + } + SaManager.getSaTokenDao().setObject(splicingClientTokenSaveKey(ct.clientToken), ct, ct.getExpiresIn()); } /** * 持久化:ClientToken-索引 - * @param at . + * @param ct . */ public void saveClientTokenIndex(ClientTokenModel ct) { - SaManager.getSaTokenDao().set(splicingKeyClientTokenIndex(ct.clientId), ct.clientToken, ct.getExpiresIn()); + if(ct == null) { + return; + } + SaManager.getSaTokenDao().set(splicingClientTokenIndexKey(ct.clientId), ct.clientToken, ct.getExpiresIn()); } /** - * 持久化:Past-ClientToken-索引 - * @param at . + * 持久化:Past-Token-索引 + * @param ct . */ - public void savePastClientTokenIndex(ClientTokenModel ct) { - SaManager.getSaTokenDao().set(splicingKeyPastTokenIndex(ct.clientId), ct.clientToken, ct.getExpiresIn()); + public void savePastTokenIndex(ClientTokenModel ct) { + if(ct == null) { + return; + } + SaManager.getSaTokenDao().set(splicingPastTokenIndexKey(ct.clientId), ct.clientToken, ct.getExpiresIn()); + } + /** + * 持久化:用户授权记录 + * @param clientId 应用id + * @param loginId 账号id + * @param scope 权限列表(多个逗号隔开) + */ + public void saveGrantScope(String clientId, Object loginId, String scope) { + if(SaFoxUtil.isEmpty(scope) == false) { + long ttl = SaOAuth2Manager.getConfig().getAccessTokenTimeout(); + SaManager.getSaTokenDao().set(splicingGrantScopeKey(clientId, loginId), scope, ttl); + } } - // ------------------- get 数据 [OK] + // ------------------- get 数据 /** - * 获取:授权码 + * 获取:Code Model * @param code . * @return . */ public CodeModel getCode(String code) { - return (CodeModel)SaManager.getSaTokenDao().getObject(splicingKeySaveCode(code)); + if(code == null) { + return null; + } + return (CodeModel)SaManager.getSaTokenDao().getObject(splicingCodeSaveKey(code)); } /** - * 获取:授权码-Value + * 获取:Code Value * @param clientId 应用id * @param loginId 账号id * @return . */ public String getCodeValue(String clientId, Object loginId) { - return SaManager.getSaTokenDao().get(splicingKeyCodeIndex(clientId, loginId)); + return SaManager.getSaTokenDao().get(splicingCodeIndexKey(clientId, loginId)); } /** - * 获取:AccessToken + * 获取:Access-Token Model * @param accessToken . * @return . */ public AccessTokenModel getAccessToken(String accessToken) { - return (AccessTokenModel)SaManager.getSaTokenDao().getObject(splicingKeySaveAccessToken(accessToken)); + if(accessToken == null) { + return null; + } + return (AccessTokenModel)SaManager.getSaTokenDao().getObject(splicingAccessTokenSaveKey(accessToken)); } /** - * 获取:AccessToken-Value + * 获取:Access-Token Value * @param clientId 应用id * @param loginId 账号id * @return . */ public String getAccessTokenValue(String clientId, Object loginId) { - return SaManager.getSaTokenDao().get(splicingKeyAccessTokenIndex(clientId, loginId)); + return SaManager.getSaTokenDao().get(splicingAccessTokenIndexKey(clientId, loginId)); } /** - * 获取:RefreshToken + * 获取:Refresh-Token Model * @param refreshToken . * @return . */ public RefreshTokenModel getRefreshToken(String refreshToken) { - return (RefreshTokenModel)SaManager.getSaTokenDao().getObject(splicingKeySaveRefreshToken(refreshToken)); + if(refreshToken == null) { + return null; + } + return (RefreshTokenModel)SaManager.getSaTokenDao().getObject(splicingRefreshTokenSaveKey(refreshToken)); } /** - * 获取:RefreshToken-Value + * 获取:Refresh-Token Value * @param clientId 应用id * @param loginId 账号id * @return . */ public String getRefreshTokenValue(String clientId, Object loginId) { - return SaManager.getSaTokenDao().get(splicingKeyRefreshTokenIndex(clientId, loginId)); + return SaManager.getSaTokenDao().get(splicingRefreshTokenIndexKey(clientId, loginId)); } - /** - * 获取:ClientTokenModel + * 获取:Client-Token Model * @param clientToken . * @return . */ public ClientTokenModel getClientToken(String clientToken) { - return (ClientTokenModel)SaManager.getSaTokenDao().getObject(splicingKeySaveClientToken(clientToken)); + if(clientToken == null) { + return null; + } + return (ClientTokenModel)SaManager.getSaTokenDao().getObject(splicingClientTokenSaveKey(clientToken)); } /** - * 获取:ClientTokenModel-Value + * 获取:Client-Token Value * @param clientId 应用id - * @param loginId 账号id * @return . */ public String getClientTokenValue(String clientId) { - return SaManager.getSaTokenDao().get(splicingKeyClientTokenIndex(clientId)); + return SaManager.getSaTokenDao().get(splicingClientTokenIndexKey(clientId)); } /** - * 获取:ClientTokenModel-Value + * 获取:Past-Token Value * @param clientId 应用id - * @param loginId 账号id * @return . */ public String getPastTokenValue(String clientId) { - return SaManager.getSaTokenDao().get(splicingKeyPastTokenIndex(clientId)); + return SaManager.getSaTokenDao().get(splicingPastTokenIndexKey(clientId)); + } + /** + * 获取:用户授权记录 + * @param clientId 应用id + * @param loginId 账号id + * @return 权限 + */ + public String getGrantScope(String clientId, Object loginId) { + return SaManager.getSaTokenDao().get(splicingGrantScopeKey(clientId, loginId)); } - - // ------------------- delete 数据 [OK] + // ------------------- delete数据 /** - * 删除:授权码 - * @param code 授权码 + * 删除:Code + * @param code 值 */ public void deleteCode(String code) { - SaManager.getSaTokenDao().deleteObject(splicingKeySaveCode(code)); + if(code != null) { + SaManager.getSaTokenDao().deleteObject(splicingCodeSaveKey(code)); + } } /** - * 删除:授权码索引 + * 删除:Code索引 * @param clientId 应用id * @param loginId 账号id */ public void deleteCodeIndex(String clientId, Object loginId) { - SaManager.getSaTokenDao().delete(splicingKeyCodeIndex(clientId, loginId)); + SaManager.getSaTokenDao().delete(splicingCodeIndexKey(clientId, loginId)); } /** - * 删除:AccessToken - * @param accessToken . + * 删除:Access-Token + * @param accessToken 值 */ public void deleteAccessToken(String accessToken) { - SaManager.getSaTokenDao().deleteObject(splicingKeySaveAccessToken(accessToken)); + if(accessToken != null) { + SaManager.getSaTokenDao().deleteObject(splicingAccessTokenSaveKey(accessToken)); + } } /** - * 删除:AccessToken索引 + * 删除:Access-Token索引 * @param clientId 应用id * @param loginId 账号id */ public void deleteAccessTokenIndex(String clientId, Object loginId) { - SaManager.getSaTokenDao().delete(splicingKeyAccessTokenIndex(clientId, loginId)); + SaManager.getSaTokenDao().delete(splicingAccessTokenIndexKey(clientId, loginId)); } /** - * 删除:RefreshAccess - * @param refreshAccess . + * 删除:Refresh-Token + * @param refreshToken 值 */ - public void deleteRefreshAccess(String refreshAccess) { - SaManager.getSaTokenDao().deleteObject(splicingKeySaveRefreshToken(refreshAccess)); + public void deleteRefreshToken(String refreshToken) { + if(refreshToken != null) { + SaManager.getSaTokenDao().deleteObject(splicingRefreshTokenSaveKey(refreshToken)); + } } /** - * 删除:RefreshAccess索引 + * 删除:Refresh-Token索引 * @param clientId 应用id * @param loginId 账号id */ - public void deleteRefreshAccessIndex(String clientId, Object loginId) { - SaManager.getSaTokenDao().delete(splicingKeyRefreshTokenIndex(clientId, loginId)); + public void deleteRefreshTokenIndex(String clientId, Object loginId) { + SaManager.getSaTokenDao().delete(splicingRefreshTokenIndexKey(clientId, loginId)); } /** - * 删除:ClientToken - * @param clientToken . + * 删除:Client-Token + * @param clientToken 值 */ public void deleteClientToken(String clientToken) { - SaManager.getSaTokenDao().deleteObject(splicingKeySaveClientToken(clientToken)); + if(clientToken != null) { + SaManager.getSaTokenDao().deleteObject(splicingClientTokenSaveKey(clientToken)); + } } /** - * 删除:ClientToken索引 + * 删除:Client-Token索引 * @param clientId 应用id */ public void deleteClientTokenIndex(String clientId) { - SaManager.getSaTokenDao().delete(splicingKeyClientTokenIndex(clientId)); + SaManager.getSaTokenDao().delete(splicingClientTokenIndexKey(clientId)); } /** * 删除:Past-Token索引 * @param clientId 应用id */ public void deletePastTokenIndex(String clientId) { - SaManager.getSaTokenDao().delete(splicingKeyPastTokenIndex(clientId)); + SaManager.getSaTokenDao().delete(splicingPastTokenIndexKey(clientId)); + } + /** + * 删除:用户授权记录 + * @param clientId 应用id + * @param loginId 账号id + */ + public void deleteGrantScope(String clientId, Object loginId) { + SaManager.getSaTokenDao().delete(splicingGrantScopeKey(clientId, loginId)); } - - // ------------------- Random数据 [OK] + // ------------------- Random数据 /** - * 生成授权码 + * 随机一个 Code * @param clientId 应用id * @param loginId 账号id * @param scope 权限 - * @return 授权码 + * @return Code */ public String randomCode(String clientId, Object loginId, String scope) { return SaFoxUtil.getRandomString(60); } /** - * 生成AccessToken - * @param codeModel CodeModel对象 - * @return AccessToken + * 随机一个 Access-Token + * @param clientId 应用id + * @param loginId 账号id + * @param scope 权限 + * @return Access-Token */ public String randomAccessToken(String clientId, Object loginId, String scope) { return SaFoxUtil.getRandomString(60); } /** - * 生成RefreshToken - * @param codeModel CodeModel对象 - * @return RefreshToken + * 随机一个 Refresh-Token + * @param clientId 应用id + * @param loginId 账号id + * @param scope 权限 + * @return Refresh-Token */ public String randomRefreshToken(String clientId, Object loginId, String scope) { return SaFoxUtil.getRandomString(60); } /** - * 生成ClientToken - * @return RefreshToken + * 随机一个 Client-Token + * @param clientId 应用id + * @param loginId 账号id + * @param scope 权限 + * @return Client-Token */ public String randomClientToken(String clientId, Object loginId) { return SaFoxUtil.getRandomString(60); } - // ------------------- 拼接key [OK] + // ------------------- 拼接key /** - * 拼接key:授权码持久化 + * 拼接key:Code持久化 * @param code 授权码 * @return key */ - public String splicingKeySaveCode(String code) { + public String splicingCodeSaveKey(String code) { return SaManager.getConfig().getTokenName() + ":oauth2:code:" + code; } /** - * 拼接key:授权码索引 + * 拼接key:Code索引 * @param clientId 应用id * @param loginId 账号id * @return key */ - public String splicingKeyCodeIndex(String clientId, Object loginId) { + public String splicingCodeIndexKey(String clientId, Object loginId) { return SaManager.getConfig().getTokenName() + ":oauth2:code-index:" + clientId + ":" + loginId; } /** @@ -755,7 +810,7 @@ public class SaOAuth2Template { * @param accessToken accessToken * @return key */ - public String splicingKeySaveAccessToken(String accessToken) { + public String splicingAccessTokenSaveKey(String accessToken) { return SaManager.getConfig().getTokenName() + ":oauth2:access-token:" + accessToken; } /** @@ -764,49 +819,58 @@ public class SaOAuth2Template { * @param loginId 账号id * @return key */ - public String splicingKeyAccessTokenIndex(String clientId, Object loginId) { + public String splicingAccessTokenIndexKey(String clientId, Object loginId) { return SaManager.getConfig().getTokenName() + ":oauth2:access-token-index:" + clientId + ":" + loginId; } /** - * 拼接key:RefreshToken持久化 + * 拼接key:Refresh-Token持久化 * @param refreshToken refreshToken * @return key */ - public String splicingKeySaveRefreshToken(String refreshToken) { + public String splicingRefreshTokenSaveKey(String refreshToken) { return SaManager.getConfig().getTokenName() + ":oauth2:refresh-token:" + refreshToken; } /** - * 拼接key:RefreshToken索引 + * 拼接key:Refresh-Token索引 * @param clientId 应用id * @param loginId 账号id * @return key */ - public String splicingKeyRefreshTokenIndex(String clientId, Object loginId) { + public String splicingRefreshTokenIndexKey(String clientId, Object loginId) { return SaManager.getConfig().getTokenName() + ":oauth2:refresh-token-index:" + clientId + ":" + loginId; } /** * 拼接key:Client-Token持久化 - * @param clientToken accessToken + * @param clientToken clientToken * @return key */ - public String splicingKeySaveClientToken(String clientToken) { + public String splicingClientTokenSaveKey(String clientToken) { return SaManager.getConfig().getTokenName() + ":oauth2:client-token:" + clientToken; } /** - * 拼接key:ClientToken 索引 - * @param clientToken accessToken + * 拼接key:Past-Token 索引 + * @param clientId clientId * @return key */ - public String splicingKeyClientTokenIndex(String clientId) { + public String splicingClientTokenIndexKey(String clientId) { return SaManager.getConfig().getTokenName() + ":oauth2:client-token-indedx:" + clientId; } /** - * 拼接key:Past-ClientToken 索引 + * 拼接key:Past-Token 索引 * @param clientId clientId * @return key */ - public String splicingKeyPastTokenIndex(String clientId) { + public String splicingPastTokenIndexKey(String clientId) { return SaManager.getConfig().getTokenName() + ":oauth2:past-token-indedx:" + clientId; } + /** + * 拼接key:用户授权记录 + * @param clientId 应用id + * @param loginId 账号id + * @return key + */ + public String splicingGrantScopeKey(String clientId, Object loginId) { + return SaManager.getConfig().getTokenName() + ":oauth2:grant-scope:" + clientId + ":" + loginId; + } } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Util.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Util.java index d604e4d2..472b055c 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Util.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/logic/SaOAuth2Util.java @@ -1,199 +1,39 @@ package cn.dev33.satoken.oauth2.logic; -import java.util.List; - import cn.dev33.satoken.context.model.SaRequest; import cn.dev33.satoken.oauth2.model.AccessTokenModel; import cn.dev33.satoken.oauth2.model.ClientTokenModel; import cn.dev33.satoken.oauth2.model.CodeModel; import cn.dev33.satoken.oauth2.model.RefreshTokenModel; import cn.dev33.satoken.oauth2.model.RequestAuthModel; +import cn.dev33.satoken.oauth2.model.SaClientModel; /** - * sa-token-oauth2 模块 静态类接口转发, 方便调用 + * Sa-Token-OAuth2 模块 工具类 * @author kong - * + * */ public class SaOAuth2Util { + /** + * 模板代码对象 + */ public static SaOAuth2Template saOAuth2Template = new SaOAuth2Template(); - /** - * 根据 SaRequest 对象创建 RequestAuthModel - * @param req SaRequest对象 - * @param loginId 账号id - * @return RequestAuthModel对象 - */ - public static RequestAuthModel generateRequestAuth(SaRequest req, Object loginId) { - return saOAuth2Template.generateRequestAuth(req, loginId); - } - - - - - - - - - - // ---------------------------------------------- 分界线 ----------------------------------------------------- - - - // ------------------- 获取数据 + // ------------------- 资源获取 /** - * 返回此平台所有权限集合 - * @return 此平台所有权限名称集合 - */ - public static List getAppScopeList() { - return saOAuth2Template.getAppScopeList(); - } - - /** - * 返回指定Client签约的所有Scope名称集合 - * @param clientId 应用id - * @return Scope集合 - */ - public static List getClientScopeList(String clientId) { - return saOAuth2Template.getClientScopeList(clientId); - } - - /** - * 获取指定 LoginId 对指定 Client 已经授权过的所有 Scope - * @param clientId 应用id - * @param loginId 账号id - * @return Scope集合 - */ - public static List getGrantScopeList(Object loginId, String clientId) { - return saOAuth2Template.getGrantScopeList(loginId, clientId); - } - - - // ------------------- 数据校验 - - /** - * [OK] 判断:该Client是否签约了指定的Scope + * 根据id获取Client信息, 如果Client为空,则抛出异常 * @param clientId 应用id - * @param scope 权限 + * @return ClientModel */ - public static boolean isContract(String clientId, String scope) { - return saOAuth2Template.isContract(clientId, scope); + public static SaClientModel checkClientModel(String clientId) { + return saOAuth2Template.checkClientModel(clientId); } /** - * 指定 loginId 是否对一个 Client 授权给了指定 Scope - * @param clientId 应用id - * @param scope 权限 - * @param loginId 账号id - * @return 是否已经授权 - */ - public static boolean isGrant(Object loginId, String clientId, String scope) { - return saOAuth2Template.isGrant(loginId, clientId, scope); - } - - /** - * [OK] 指定Client使用指定url作为回调地址,是否合法 - * @param clientId 应用id - * @param url 指定url - * @return 是否合法 - */ - public static boolean isRightUrl(String clientId, String url) { - return saOAuth2Template.isRightUrl(clientId, url); - } - - /** - * [OK 方法名改一下]校验code、clientId、clientSecret 三者是否正确 - * @param code 授权码 - * @param clientId 应用id - * @param clientSecret 秘钥 - * @param redirectUri 秘钥 - * @return CodeModel对象 - */ - public static CodeModel checkCodeIdSecret(String code, String clientId, String clientSecret, String redirectUri) { - return saOAuth2Template.checkCodeIdSecret(code, clientId, clientSecret, redirectUri); - } - - /** - * [default] 校验access_token、clientId、clientSecret 三者是否正确 - * @param accessToken access_token - * @param clientId 应用id - * @param clientSecret 秘钥 - * @return AccessTokenModel对象 - */ - public static AccessTokenModel checkTokenIdSecret(String accessToken, String clientId, String clientSecret) { - return saOAuth2Template.checkTokenIdSecret(accessToken, clientId, clientSecret); - } - - - - // ------------------- 逻辑相关 - - /** - * [OK] 根据参数生成一个授权码并返回 - * @param authModel 请求授权参数Model - * @return 授权码Model - */ - public static CodeModel generateCode(RequestAuthModel authModel) { - return saOAuth2Template.generateCode(authModel); - } - - - /** - * 根据授权码获得授权码Model - * @param code 授权码 - * @return 授权码Model - */ - public static CodeModel getCode(String code) { - return saOAuth2Template.getCode(code); - } - - /** - * [default] 删除一个授权码 - * @param code 授权码 - */ - public static void deleteCode(String code) { - saOAuth2Template.deleteCode(code); - } - - /** - * 根据授权码Model生成一个access_token - * @param codeModel 授权码Model - * @return AccessTokenModel - */ - public static AccessTokenModel generateAccessToken(String code) { - return saOAuth2Template.generateAccessToken(code); - } - - /** - * [default] 根据 access_token 获得其Model详细信息 - * @param accessToken access_token - * @return AccessTokenModel (授权码Model) - */ - public static AccessTokenModel getAccessToken(String accessToken) { - return saOAuth2Template.getAccessToken(accessToken); - } - - /** - * 根据 refresh_token 生成一个新的 access_token - * @param refreshToken refresh_token - * @return 新的 access_token - */ - public static AccessTokenModel refreshAccessToken(String refreshToken) { - return saOAuth2Template.refreshAccessToken(refreshToken); - } - - /** - * [default] 根据 refresh_token 获得其Model详细信息 - * @param refreshToken refresh_token - * @return RefreshToken - */ - public static RefreshTokenModel getRefreshToken(String refreshToken) { - return saOAuth2Template.getRefreshToken(refreshToken); - } - - /** - * [default] 获取 access_token 所代表的LoginId + * 获取 access_token 所代表的LoginId * @param accessToken access_token * @return LoginId */ @@ -202,27 +42,85 @@ public class SaOAuth2Util { } /** - * 构建:AccessToken Model (根据RequestAuthModel) 用于隐藏式 - * @param ra 请求授权参数Model - * @return 授权码Model + * 获取 Access-Token,如果AccessToken为空则抛出异常 + * @param accessToken . + * @return . */ - public static AccessTokenModel generateAccessToken(RequestAuthModel ra) { - return saOAuth2Template.generateAccessToken(ra); + public static AccessTokenModel checkAccessToken(String accessToken) { + return saOAuth2Template.checkAccessToken(accessToken); + } + + /** + * 获取 Client-Token,如果ClientToken为空则抛出异常 + * @param clientToken . + * @return . + */ + public static ClientTokenModel checkClientToken(String clientToken) { + return saOAuth2Template.checkClientToken(clientToken); + } + + + // ------------------- generate 构建数据 + + /** + * 构建Model:请求Model + * @param req SaRequest对象 + * @param loginId 账号id + * @return RequestAuthModel对象 + */ + public static RequestAuthModel generateRequestAuth(SaRequest req, Object loginId) { + return saOAuth2Template.generateRequestAuth(req, loginId); + } + + /** + * 构建Model:Code授权码 + * @param ra 请求参数Model + * @return 授权码Model + */ + public static CodeModel generateCode(RequestAuthModel ra) { + return saOAuth2Template.generateCode(ra); + } + + /** + * 构建Model:Access-Token + * @param code 授权码Model + * @return AccessToken Model + */ + public static AccessTokenModel generateAccessToken(String code) { + return saOAuth2Template.generateAccessToken(code); } /** - * 构建:ClientToken Model - * @param ra 请求授权参数Model - * @return ClientToken-Model + * 刷新Model:根据 Refresh-Token 生成一个新的 Access-Token + * @param refreshToken Refresh-Token值 + * @return 新的 Access-Token + */ + public static AccessTokenModel refreshAccessToken(String refreshToken) { + return saOAuth2Template.refreshAccessToken(refreshToken); + } + + /** + * 构建Model:Access-Token (根据RequestAuthModel构建,用于隐藏式 and 密码式) + * @param ra 请求参数Model + * @param isCreateRt 是否生成对应的Refresh-Token + * @return Access-Token Model + */ + public static AccessTokenModel generateAccessToken(RequestAuthModel ra, boolean isCreateRt) { + return saOAuth2Template.generateAccessToken(ra, isCreateRt); + } + + /** + * 构建Model:Client-Token + * @param clientId 应用id + * @param scope 授权范围 + * @return Client-Token Model */ public static ClientTokenModel generateClientToken(String clientId, String scope) { return saOAuth2Template.generateClientToken(clientId, scope); } - - // ------------------- 自定义策略相关 - + /** - * [OK] 构建URL:下放授权码URL + * 构建URL:下放Code URL (Authorization Code 授权码) * @param redirectUri 下放地址 * @param code code参数 * @param state state参数 @@ -231,16 +129,144 @@ public class SaOAuth2Util { public static String buildRedirectUri(String redirectUri, String code, String state) { return saOAuth2Template.buildRedirectUri(redirectUri, code, state); } + /** - * [OK] 构建URL:下放Token URL + * 构建URL:下放Access-Token URL (implicit 隐藏式) * @param redirectUri 下放地址 * @param token token * @param state state参数 * @return 构建完毕的URL */ - public static String buildRedirectUri2(String redirectUri, String token, String state) { - return saOAuth2Template.buildRedirectUri2(redirectUri, token, state); + public static String buildImplicitRedirectUri(String redirectUri, String token, String state) { + return saOAuth2Template.buildImplicitRedirectUri(redirectUri, token, state); } + // ------------------- 数据校验 + + /** + * 判断:指定 loginId 是否对一个 Client 授权给了指定 Scope + * @param loginId 账号id + * @param clientId 应用id + * @param scope 权限 + * @return 是否已经授权 + */ + public static boolean isGrant(Object loginId, String clientId, String scope) { + return saOAuth2Template.isGrant(loginId, clientId, scope); + } + + /** + * 校验:该Client是否签约了指定的Scope + * @param clientId 应用id + * @param scope 权限(多个用逗号隔开) + */ + public static void checkContract(String clientId, String scope) { + saOAuth2Template.checkContract(clientId, scope); + } + + /** + * 校验:该Client使用指定url作为回调地址,是否合法 + * @param clientId 应用id + * @param url 指定url + */ + public static void checkRightUrl(String clientId, String url) { + saOAuth2Template.checkRightUrl(clientId, url); + } + /** + * 校验:clientId 与 clientSecret 是否正确 + * @param clientId 应用id + * @param clientSecret 秘钥 + * @return SaClientModel对象 + */ + public static SaClientModel checkClientSecret(String clientId, String clientSecret) { + return saOAuth2Template.checkClientSecret(clientId, clientSecret); + } + + /** + * 校验:使用 code 获取 token 时提供的参数校验 + * @param code 授权码 + * @param clientId 应用id + * @param clientSecret 秘钥 + * @param redirectUri 重定向地址 + * @return CodeModel对象 + */ + public static CodeModel checkGainTokenParam(String code, String clientId, String clientSecret, String redirectUri) { + return saOAuth2Template.checkGainTokenParam(code, clientId, clientSecret, redirectUri); + } + + /** + * 校验:使用 Refresh-Token 刷新 Access-Token 时提供的参数校验 + * @param clientId 应用id + * @param clientSecret 秘钥 + * @param refreshToken Refresh-Token + * @return CodeModel对象 + */ + public static RefreshTokenModel checkRefreshTokenParam(String clientId, String clientSecret, String refreshToken) { + return saOAuth2Template.checkRefreshTokenParam(clientId, clientSecret, refreshToken); + } + + + // ------------------- save 数据 + + /** + * 持久化:用户授权记录 + * @param clientId 应用id + * @param loginId 账号id + * @param scope 权限列表(多个逗号隔开) + */ + public static void saveGrantScope(String clientId, Object loginId, String scope) { + saOAuth2Template.saveGrantScope(clientId, loginId, scope); + } + + + // ------------------- get 数据 + + /** + * 获取:Code Model + * @param code . + * @return . + */ + public static CodeModel getCode(String code) { + return saOAuth2Template.getCode(code); + } + + /** + * 获取:Access-Token Model + * @param accessToken . + * @return . + */ + public static AccessTokenModel getAccessToken(String accessToken) { + return saOAuth2Template.getAccessToken(accessToken); + } + + /** + * 获取:Refresh-Token Model + * @param refreshToken . + * @return . + */ + public static RefreshTokenModel getRefreshToken(String refreshToken) { + return saOAuth2Template.getRefreshToken(refreshToken); + } + + /** + * 获取:Client-Token Model + * @param clientToken . + * @return . + */ + public static ClientTokenModel getClientToken(String clientToken) { + return saOAuth2Template.getClientToken(clientToken); + } + + /** + * 获取:用户授权记录 + * @param clientId 应用id + * @param loginId 账号id + * @return 权限 + */ + public static String getGrantScope(String clientId, Object loginId) { + return saOAuth2Template.getGrantScope(clientId, loginId); + } + + + } diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/AccessTokenModel.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/AccessTokenModel.java index 211dc0d1..eba671e8 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/AccessTokenModel.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/AccessTokenModel.java @@ -1,32 +1,35 @@ package cn.dev33.satoken.oauth2.model; +import java.io.Serializable; import java.util.LinkedHashMap; import java.util.Map; /** - * Model: access_token + * Model: Access-Token * @author kong * */ -public class AccessTokenModel { +public class AccessTokenModel implements Serializable { + + private static final long serialVersionUID = -6541180061782004705L; /** - * access_token 值 + * Access-Token 值 */ public String accessToken; /** - * refresh_token 值 + * Refresh-Token 值 */ public String refreshToken; /** - * access_token 到期时间 + * Access-Token 到期时间 */ public long expiresTime; /** - * refresh_token 到期时间 + * Refresh-Token 到期时间 */ public long refreshExpiresTime; @@ -92,7 +95,6 @@ public class AccessTokenModel { return s < 1 ? -2 : s; } - /** * 将所有属性转换为下划线形式的Map * @return diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/ClientTokenModel.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/ClientTokenModel.java index 5ac2ea49..52c67deb 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/ClientTokenModel.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/ClientTokenModel.java @@ -1,22 +1,25 @@ package cn.dev33.satoken.oauth2.model; +import java.io.Serializable; import java.util.LinkedHashMap; import java.util.Map; /** - * Model: client_token + * Model: Client-Token * @author kong * */ -public class ClientTokenModel { +public class ClientTokenModel implements Serializable { + + private static final long serialVersionUID = -6541180061782004705L; /** - * client_token 值 + * Client-Token 值 */ public String clientToken; /** - * client_token 到期时间 + * Client-Token 到期时间 */ public long expiresTime; diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/CodeModel.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/CodeModel.java index 998da143..a3d86382 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/CodeModel.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/CodeModel.java @@ -1,11 +1,15 @@ package cn.dev33.satoken.oauth2.model; +import java.io.Serializable; + /** - * Model: [授权码 - 数据 对应关系] + * Model: 授权码 * @author kong * */ -public class CodeModel { +public class CodeModel implements Serializable { + + private static final long serialVersionUID = -6541180061782004705L; /** * 授权码 diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/RefreshTokenModel.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/RefreshTokenModel.java index 2b945a3c..4414d56d 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/RefreshTokenModel.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/RefreshTokenModel.java @@ -1,18 +1,23 @@ package cn.dev33.satoken.oauth2.model; + +import java.io.Serializable; + /** - * Model: refresh_token + * Model: Refresh-Token * @author kong * */ -public class RefreshTokenModel { +public class RefreshTokenModel implements Serializable { + + private static final long serialVersionUID = -6541180061782004705L; /** - * refresh_token 值 + * Refresh-Token 值 */ public String refreshToken; /** - * refresh_token到期时间 + * Refresh-Token 到期时间 */ public long expiresTime; diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/RequestAuthModel.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/RequestAuthModel.java index 61c1ce9d..7dcb3775 100644 --- a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/RequestAuthModel.java +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/RequestAuthModel.java @@ -1,5 +1,7 @@ package cn.dev33.satoken.oauth2.model; +import java.io.Serializable; + import cn.dev33.satoken.exception.SaTokenException; import cn.dev33.satoken.util.SaFoxUtil; @@ -8,7 +10,9 @@ import cn.dev33.satoken.util.SaFoxUtil; * @author kong * */ -public class RequestAuthModel { +public class RequestAuthModel implements Serializable { + + private static final long serialVersionUID = -6541180061782004705L; /** * 应用id diff --git a/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/SaClientModel.java b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/SaClientModel.java new file mode 100644 index 00000000..952f696c --- /dev/null +++ b/sa-token-plugin/sa-token-oauth2/src/main/java/cn/dev33/satoken/oauth2/model/SaClientModel.java @@ -0,0 +1,115 @@ +package cn.dev33.satoken.oauth2.model; + +import java.io.Serializable; + +/** + * Client应用信息 Model + * @author kong + * + */ +public class SaClientModel implements Serializable { + + private static final long serialVersionUID = -6541180061782004705L; + + /** + * 应用id + */ + public String clientId; + + /** + * 应用秘钥 + */ + public String clientSecret; + + /** + * 应用签约的所有权限, 多个用逗号隔开 + */ + public String contractScope; + + /** + * 应用允许授权的所有URL, 多个用逗号隔开 + */ + public String allowUrl; + + public SaClientModel() { + + } + public SaClientModel(String clientId, String clientSecret, String contractScope, String allowUrl) { + super(); + this.clientId = clientId; + this.clientSecret = clientSecret; + this.contractScope = contractScope; + this.allowUrl = allowUrl; + } + + /** + * @return 应用id + */ + public String getClientId() { + return clientId; + } + + /** + * @param clientId 应用id + * @return 对象自身 + */ + public SaClientModel setClientId(String clientId) { + this.clientId = clientId; + return this; + } + + /** + * @return 应用秘钥 + */ + public String getClientSecret() { + return clientSecret; + } + + /** + * @param clientSecret 应用秘钥 + * @return 对象自身 + */ + public SaClientModel setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + return this; + } + + /** + * @return 应用签约的所有权限, 多个用逗号隔开 + */ + public String getContractScope() { + return contractScope; + } + + /** + * @param contractScope 应用签约的所有权限, 多个用逗号隔开 + * @return 对象自身 + */ + public SaClientModel setContractScope(String contractScope) { + this.contractScope = contractScope; + return this; + } + + /** + * @return 应用允许授权的所有URL, 多个用逗号隔开 + */ + public String getAllowUrl() { + return allowUrl; + } + + /** + * @param allowUrl 应用允许授权的所有URL, 多个用逗号隔开 + * @return 对象自身 + */ + public SaClientModel setAllowUrl(String allowUrl) { + this.allowUrl = allowUrl; + return this; + } + + @Override + public String toString() { + return "SaClientModel [clientId=" + clientId + ", clientSecret=" + clientSecret + ", contractScope=" + + contractScope + ", allowUrl=" + allowUrl + "]"; + } + +} diff --git a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaBeanInject.java b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaBeanInject.java index d456bf5c..c33f0e07 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaBeanInject.java +++ b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/java/cn/dev33/satoken/reactor/spring/SaBeanInject.java @@ -2,8 +2,6 @@ package cn.dev33.satoken.reactor.spring; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.annotation.Import; -import org.springframework.stereotype.Component; import org.springframework.util.PathMatcher; import cn.dev33.satoken.SaManager; @@ -21,8 +19,6 @@ import cn.dev33.satoken.temp.SaTempInterface; * @author kong * */ -@Component -@Import({SaHistoryVersionInject.class, SaBeanRegister.class}) public class SaBeanInject { /** diff --git a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/resources/META-INF/spring.factories b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/resources/META-INF/spring.factories index 399afb5a..cecda683 100644 --- a/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/resources/META-INF/spring.factories +++ b/sa-token-starter/sa-token-reactor-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -1 +1,4 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.dev33.satoken.reactor.spring.SaBeanInject \ No newline at end of file +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +cn.dev33.satoken.reactor.spring.SaBeanRegister,\ +cn.dev33.satoken.reactor.spring.SaBeanInject,\ +cn.dev33.satoken.reactor.spring.SaHistoryVersionInject \ No newline at end of file diff --git a/sa-token-starter/sa-token-spring-boot-starter/pom.xml b/sa-token-starter/sa-token-spring-boot-starter/pom.xml index ba0ffd58..582e408a 100644 --- a/sa-token-starter/sa-token-spring-boot-starter/pom.xml +++ b/sa-token-starter/sa-token-spring-boot-starter/pom.xml @@ -26,6 +26,13 @@ spring-boot-starter-web 2.0.0.RELEASE + + + cn.dev33 + sa-token-oauth2 + ${sa-token-version} + true + org.springframework.boot diff --git a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaBeanInject.java b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaBeanInject.java index a59b01de..887b47ef 100644 --- a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaBeanInject.java +++ b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/SaBeanInject.java @@ -2,8 +2,6 @@ package cn.dev33.satoken.spring; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.annotation.Import; -import org.springframework.stereotype.Component; import org.springframework.util.PathMatcher; import cn.dev33.satoken.SaManager; @@ -21,8 +19,6 @@ import cn.dev33.satoken.temp.SaTempInterface; * @author kong * */ -@Component -@Import({SaBeanRegister.class, SaHistoryVersionInject.class}) public class SaBeanInject { /** diff --git a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/oauth2/SaOAuth2BeanInject.java b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/oauth2/SaOAuth2BeanInject.java new file mode 100644 index 00000000..fd0380e8 --- /dev/null +++ b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/oauth2/SaOAuth2BeanInject.java @@ -0,0 +1,40 @@ +package cn.dev33.satoken.spring.oauth2; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; + +import cn.dev33.satoken.oauth2.SaOAuth2Manager; +import cn.dev33.satoken.oauth2.config.SaOAuth2Config; +import cn.dev33.satoken.oauth2.logic.SaOAuth2Template; +import cn.dev33.satoken.oauth2.logic.SaOAuth2Util; + +/** + * 注入 Sa-Token-OAuth2 所需要的Bean + * + * @author kong + * + */ +@ConditionalOnClass(SaOAuth2Manager.class) +public class SaOAuth2BeanInject { + + /** + * 注入OAuth2配置Bean + * + * @param saOAuth2Config 配置对象 + */ + @Autowired(required = false) + public void setSaOAuth2Config(SaOAuth2Config saOAuth2Config) { + SaOAuth2Manager.setConfig(saOAuth2Config); + } + + /** + * 注入代码模板Bean + * + * @param saOAuth2Template 代码模板Bean + */ + @Autowired(required = false) + public void setSaOAuth2Interface(SaOAuth2Template saOAuth2Template) { + SaOAuth2Util.saOAuth2Template = saOAuth2Template; + } + +} diff --git a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/oauth2/SaOAuth2BeanRegister.java b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/oauth2/SaOAuth2BeanRegister.java new file mode 100644 index 00000000..0122d0a2 --- /dev/null +++ b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/oauth2/SaOAuth2BeanRegister.java @@ -0,0 +1,28 @@ +package cn.dev33.satoken.spring.oauth2; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; + +import cn.dev33.satoken.oauth2.SaOAuth2Manager; +import cn.dev33.satoken.oauth2.config.SaOAuth2Config; + +/** + * 注册 Sa-Token-OAuth2 所需要的Bean + * @author kong + * + */ +@ConditionalOnClass(SaOAuth2Manager.class) +public class SaOAuth2BeanRegister { + + /** + * 获取OAuth2配置Bean + * @return 配置对象 + */ + @Bean + @ConfigurationProperties(prefix = "sa-token.oauth2") + public SaOAuth2Config getSaOAuth2Config() { + return new SaOAuth2Config(); + } + +} diff --git a/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/oauth2/package-info.java b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/oauth2/package-info.java new file mode 100644 index 00000000..793d87db --- /dev/null +++ b/sa-token-starter/sa-token-spring-boot-starter/src/main/java/cn/dev33/satoken/spring/oauth2/package-info.java @@ -0,0 +1,4 @@ +/** + * Sa-Token-OAuth2 模块自动化配置(只有引入了Sa-Token-OAuth2模块后,此包下的代码才会开始工作) + */ +package cn.dev33.satoken.spring.oauth2; \ No newline at end of file diff --git a/sa-token-starter/sa-token-spring-boot-starter/src/main/resources/META-INF/spring.factories b/sa-token-starter/sa-token-spring-boot-starter/src/main/resources/META-INF/spring.factories index bc9a42c0..2e7e333a 100644 --- a/sa-token-starter/sa-token-spring-boot-starter/src/main/resources/META-INF/spring.factories +++ b/sa-token-starter/sa-token-spring-boot-starter/src/main/resources/META-INF/spring.factories @@ -1 +1,6 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=cn.dev33.satoken.spring.SaBeanInject \ No newline at end of file +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +cn.dev33.satoken.spring.SaBeanRegister,\ +cn.dev33.satoken.spring.SaBeanInject,\ +cn.dev33.satoken.spring.SaHistoryVersionInject,\ +cn.dev33.satoken.spring.oauth2.SaOAuth2BeanRegister,\ +cn.dev33.satoken.spring.oauth2.SaOAuth2BeanInject \ No newline at end of file