新增OAuth2.0接口

This commit is contained in:
click33
2021-07-23 03:21:03 +08:00
parent bea2592dc9
commit bc8339e13d
7 changed files with 166 additions and 24 deletions

View File

@@ -30,6 +30,7 @@
<div>当前Openid <span class="openid"></span></div>
<div>当前Access-Token <span class="access_token"></span></div>
<div>当前Refresh-Token <span class="refresh_token"></span></div>
<div>当前令牌包含Scope <span class="scope"></span></div>
<div>当前Client-Token <span class="client_token"></span></div>
</div>
<div class="btn-box">
@@ -58,7 +59,7 @@
<code>http://sa-oauth-server.com:8001/oauth2/refresh?grant_type=refresh_token&client_id={value}&client_secret={value}&refresh_token={value}</code>
<button onclick="getUserinfo()">获取账号信息</button>
<span class="ps">使用 Access-Token 置换资源: 获取账号昵称、头像、性别等信息 </span>
<span class="ps">使用 Access-Token 置换资源: 获取账号昵称、头像、性别等信息 (Access-Token具备userinfo权限时才可以获取成功) </span>
<code>http://sa-oauth-server.com:8001/oauth2/userinfo?access_token={value}</code>
<br>
@@ -202,7 +203,11 @@
data: {accessToken: accessToken},
dataType: 'json',
success: function(res) {
layer.alert(JSON.stringify(res.data));
if(res.code == 200) {
layer.alert(JSON.stringify(res.data));
} else {
layer.alert(res.msg);
}
},
error: function(xhr, type, errorThrown){
return layer.alert("异常:" + JSON.stringify(xhr));

View File

@@ -76,6 +76,9 @@ public class SaOAuth2ServerController {
Object loginId = SaOAuth2Util.getLoginIdByAccessToken(accessToken);
System.out.println("-------- 此Access-Token对应的账号id: " + loginId);
// 校验 Access-Token 是否具有权限: userinfo
SaOAuth2Util.checkScope(accessToken, "userinfo");
// 模拟账号信息 (真实环境需要查询数据库获取信息)
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("nickname", "shengzhang_");

View File

@@ -105,7 +105,35 @@ http://sa-oauth-server.com:8001/oauth2/refresh
接口返回值同章节1.2,此处不再赘述
### 1.4、根据 Access-Token 获取相应用户的账号信息
### 1.4、回收 Access-Token (如果需要的话)
在Access-Token过期前主动将其回收
``` url
http://sa-oauth-server.com:8001/oauth2/revoke
?client_id={value}
&client_secret={value}
&access_token={value}
```
参数详解:
| 参数 | 是否必填 | 说明 |
| :-------- | :-------- | :-------- |
| client_id | 是 | 应用id |
| client_secret | 是 | 应用秘钥 |
| access_token | 是 | 步骤1.2中获取到的`Access-Token`值 |
返回值样例:
``` js
{
"code": 200,
"msg": "ok",
"data": null
}
```
### 1.5、根据 Access-Token 获取相应用户的账号信息
注:此接口为官方仓库模拟接口,正式项目中大家可以根据此样例,自定义需要的接口及参数
``` url

View File

@@ -15,6 +15,7 @@ public class SaOAuth2Consts {
public static String authorize = "/oauth2/authorize";
public static String token = "/oauth2/token";
public static String refresh = "/oauth2/refresh";
public static String revoke = "/oauth2/revoke";
public static String client_token = "/oauth2/client_token";
public static String doLogin = "/oauth2/doLogin";
public static String doConfirm = "/oauth2/doConfirm";
@@ -33,6 +34,7 @@ public class SaOAuth2Consts {
public static String state = "state";
public static String code = "code";
public static String token = "token";
public static String access_token = "access_token";
public static String refresh_token = "refresh_token";
public static String grant_type = "grant_type";
public static String username = "username";

View File

@@ -51,6 +51,11 @@ public class SaOAuth2Handle {
if(req.isPath(Api.refresh) && req.isParam(Param.grant_type, GrantType.refresh_token)) {
return refreshToken(req);
}
// 回收 Access-Token
if(req.isPath(Api.revoke)) {
return revokeToken(req);
}
// doLogin 登录接口
if(req.isPath(Api.doLogin)) {
@@ -167,10 +172,34 @@ public class SaOAuth2Handle {
SaOAuth2Util.checkRefreshTokenParam(clientId, clientSecret, refreshToken);
// 获取新Token返回
Object data = SaOAuth2Util.saOAuth2Template.refreshAccessToken(refreshToken).toLineMap();
Object data = SaOAuth2Util.refreshAccessToken(refreshToken).toLineMap();
return SaResult.data(data);
}
/**
* 回收 Access-Token
* @param req 请求对象
* @return 处理结果
*/
public static Object revokeToken(SaRequest req) {
// 获取参数
String clientId = req.getParamNotNull(Param.client_id);
String clientSecret = req.getParamNotNull(Param.client_secret);
String accessToken = req.getParamNotNull(Param.access_token);
// 如果 Access-Token 不存在,直接返回
if(SaOAuth2Util.getAccessToken(accessToken) == null) {
return SaResult.ok("access_token不存在" + accessToken);
}
// 校验参数
SaOAuth2Util.checkAccessTokenParam(clientId, clientSecret, accessToken);
// 获取新Token返回
SaOAuth2Util.revokeAccessToken(accessToken);
return SaResult.ok();
}
/**
* doLogin 登录接口
* @param req 请求对象

View File

@@ -41,7 +41,7 @@ public class SaOAuth2Template {
return null;
}
// ------------------- 资源获取
// ------------------- 资源校验API
/**
* 根据id获取Client信息, 如果Client为空则抛出异常
* @param clientId 应用id
@@ -54,14 +54,6 @@ public class SaOAuth2Template {
}
return clientModel;
}
/**
* 获取 access_token 所代表的LoginId
* @param accessToken access_token
* @return LoginId
*/
public Object getLoginIdByAccessToken(String accessToken) {
return checkAccessToken(accessToken).loginId;
}
/**
* 获取 Access-Token如果AccessToken为空则抛出异常
* @param accessToken .
@@ -82,6 +74,29 @@ public class SaOAuth2Template {
SaOAuth2Exception.throwBy(ct == null, "无效client_token" + ct);
return ct;
}
/**
* 获取 Access-Token 所代表的LoginId
* @param accessToken Access-Token
* @return LoginId
*/
public Object getLoginIdByAccessToken(String accessToken) {
return checkAccessToken(accessToken).loginId;
}
/**
* 校验:指定 Access-Token 是否具有指定 Scope
* @param accessToken Access-Token
* @param scopes 需要校验的权限列表
*/
public void checkScope(String accessToken, String... scopes) {
if(scopes == null || scopes.length == 0) {
return;
}
AccessTokenModel at = checkAccessToken(accessToken);
List<String> scopeList = SaFoxUtil.convertStringToList(at.scope);
for (String scope : scopes) {
SaOAuth2Exception.throwBy(scopeList.contains(scope) == false, "该 Access-Token 不具备 Scope" + scope);
}
}
// ------------------- generate 构建数据
/**
@@ -277,7 +292,28 @@ public class SaOAuth2Template {
}
return url;
}
/**
* 回收 Access-Token
* @param accessToken Access-Token值
*/
public void revokeAccessToken(String accessToken) {
// 如果查不到任何东西, 直接返回
AccessTokenModel at = getAccessToken(accessToken);
if(at == null) {
return;
}
// 删除 Access-Token
deleteAccessToken(accessToken);
deleteAccessTokenIndex(at.clientId, at.accessToken);
// 删除对应的 Refresh-Token
String refreshToken = getRefreshTokenValue(at.clientId, at.loginId);
deleteRefreshToken(refreshToken);
deleteRefreshTokenIndex(at.clientId, at.loginId);
}
// ------------------- check 数据校验
/**
* 判断:指定 loginId 是否对一个 Client 授权给了指定 Scope
@@ -389,6 +425,19 @@ public class SaOAuth2Template {
// 返回Refresh-Token
return rt;
}
/**
* 校验Access-Token、clientId、clientSecret 三者是否匹配成功
* @param clientId 应用id
* @param clientSecret 秘钥
* @param accessToken Access-Token
* @return SaClientModel对象
*/
public AccessTokenModel checkAccessTokenParam(String clientId, String clientSecret, String accessToken) {
AccessTokenModel at = checkAccessToken(accessToken);
SaOAuth2Exception.throwBy(at.clientId.equals(clientId) == false, "无效client_id" + clientId);
checkClientSecret(clientId, clientSecret);
return at;
}
// ------------------- conver 数据转换
/**

View File

@@ -21,7 +21,7 @@ public class SaOAuth2Util {
public static SaOAuth2Template saOAuth2Template = new SaOAuth2Template();
// ------------------- 资源获取
// ------------------- 资源校验API
/**
* 根据id获取Client信息, 如果Client为空则抛出异常
@@ -32,15 +32,6 @@ public class SaOAuth2Util {
return saOAuth2Template.checkClientModel(clientId);
}
/**
* 获取 access_token 所代表的LoginId
* @param accessToken access_token
* @return LoginId
*/
public static Object getLoginIdByAccessToken(String accessToken) {
return saOAuth2Template.getLoginIdByAccessToken(accessToken);
}
/**
* 获取 Access-Token如果AccessToken为空则抛出异常
* @param accessToken .
@@ -58,7 +49,24 @@ public class SaOAuth2Util {
public static ClientTokenModel checkClientToken(String clientToken) {
return saOAuth2Template.checkClientToken(clientToken);
}
/**
* 获取 Access-Token 所代表的LoginId
* @param accessToken Access-Token
* @return LoginId
*/
public static Object getLoginIdByAccessToken(String accessToken) {
return saOAuth2Template.getLoginIdByAccessToken(accessToken);
}
/**
* 校验:指定 Access-Token 是否具有指定 Scope
* @param accessToken Access-Token
* @param scopes 需要校验的权限列表
*/
public static void checkScope(String accessToken, String... scopes) {
saOAuth2Template.checkScope(accessToken, scopes);
}
// ------------------- generate 构建数据
@@ -141,6 +149,13 @@ public class SaOAuth2Util {
return saOAuth2Template.buildImplicitRedirectUri(redirectUri, token, state);
}
/**
* 回收 Access-Token
* @param accessToken Access-Token值
*/
public static void revokeAccessToken(String accessToken) {
saOAuth2Template.revokeAccessToken(accessToken);
}
// ------------------- 数据校验
@@ -172,6 +187,7 @@ public class SaOAuth2Util {
public static void checkRightUrl(String clientId, String url) {
saOAuth2Template.checkRightUrl(clientId, url);
}
/**
* 校验clientId 与 clientSecret 是否正确
* @param clientId 应用id
@@ -205,6 +221,16 @@ public class SaOAuth2Util {
return saOAuth2Template.checkRefreshTokenParam(clientId, clientSecret, refreshToken);
}
/**
* 校验Access-Token、clientId、clientSecret 三者是否匹配成功
* @param clientId 应用id
* @param clientSecret 秘钥
* @param accessToken Access-Token
* @return SaClientModel对象
*/
public static AccessTokenModel checkAccessTokenParam(String clientId, String clientSecret, String accessToken) {
return saOAuth2Template.checkAccessTokenParam(clientId, clientSecret, accessToken);
}
// ------------------- save 数据