OAuth2.0 beta ..

This commit is contained in:
click33
2021-07-17 23:48:12 +08:00
parent 742b65366a
commit 971c2860f0
46 changed files with 2160 additions and 914 deletions

View File

@@ -22,26 +22,39 @@
<dependencies>
<!-- springboot依赖 -->
<!-- SpringBoot依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- sa-token 权限认证, 在线文档http://sa-token.dev33.cn/ -->
<!-- Sa-Token 权限认证, 在线文档http://sa-token.dev33.cn/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>${sa-token-version}</version>
</dependency>
<!-- thymeleaf 视图引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- OkHttps网络请求库 http://okhttps.ejlchina.com/ -->
<dependency>
<groupId>com.ejlchina</groupId>
<artifactId>okhttps</artifactId>
<version>2.4.5</version>
<groupId>com.ejlchina</groupId>
<artifactId>okhttps</artifactId>
<version>3.1.1</version>
</dependency>
<!-- 热刷新 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- ConfigurationProperties -->
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@@ -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";
}

View File

@@ -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;
}
}

View File

@@ -33,12 +33,10 @@ public class SoMap extends LinkedHashMap<String, Object> {
public SoMap() {
}
/** 以下元素会在isNull函数中被判定为Null */
public static final Object[] NULL_ELEMENT_ARRAY = {null, ""};
public static final List<Object> NULL_ELEMENT_LIST;
static {
NULL_ELEMENT_LIST = Arrays.asList(NULL_ELEMENT_ARRAY);
}
@@ -144,6 +142,22 @@ public class SoMap extends LinkedHashMap<String, Object> {
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<Object> getList(String key) {

View File

@@ -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\

View File

@@ -0,0 +1,253 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sa-OAuth2-Client端-测试页</title>
<style type="text/css">
body{background-color: #D0D9E0;}
*{margin: 0px; padding: 0px;}
.login-box{max-width: 1000px; margin: 30px auto; padding: 1em;}
.info{line-height: 30px;}
.btn-box{margin-top: 10px; margin-bottom: 15px;}
.btn-box a{margin-right: 10px;}
.btn-box a:hover{text-decoration:underline !important;}
.login-box input{line-height: 25px; margin-bottom: 10px; padding-left: 5px;}
.login-box button{padding: 5px 15px; margin-top: 20px; cursor: pointer; }
.login-box a{text-decoration: none;}
.pst{color: #666; margin-top: 15px;}
.ps{color: #666; margin-left: 10px;}
.login-box code{display: block; background-color: #F5F2F0; border: 1px #ccc solid; color: #600; padding: 15px; margin-top: 5px; border-radius: 2px; }
.info b,.info span{color: green;}
</style>
</head>
<body>
<div class="login-box">
<h2>Sa-OAuth2-Client端-测试页</h2> <br>
<div class="info">
<div>当前账号id
<b class="uid" th:utext="${uid}"></b>
</div>
<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>当前Client-Token <span class="client_token"></span></div>
</div>
<div class="btn-box">
<a href="javascript:logout();">注销</a>
<a href="/">回到首页</a>
</div>
<hr><br>
<h3>模式一授权码Authorization Code</h3>
<p class="pst">授权码OAuth2.0标准授权流程,先 (重定向) 获取Code授权码再 (Rest API) 获取 Access-Token 和 Openid </p>
<a href="http://sa-oauth-server.com:8001/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=http://sa-oauth-client.com:8002/">
<button>点我开始授权登录(静默授权)</button>
</a>
<span class="ps">当请求链接不包含scope权限时将无需用户手动确认做到静默授权当然此时我们也只能获取openid</span>
<code>http://sa-oauth-server.com:8001/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=http://sa-oauth-client.com:8002/</code>
<a href="http://sa-oauth-server.com:8001/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=http://sa-oauth-client.com:8002/&scope=userinfo">
<button>授权登录(显式授权)</button>
</a>
<span class="ps">当请求链接包含具体的scope权限时将需要用户手动确认此时我们除了openid以外还可以获取更多的资源</span>
<code>http://sa-oauth-server.com:8001/oauth2/authorize?response_type=code&client_id=1001&redirect_uri=http://sa-oauth-client.com:8002/&scope=userinfo</code>
<button onclick="refreshToken()">刷新令牌</button>
<span class="ps">我们可以拿着 Refresh-Token 去刷新我们的 Access-Token每次刷新后旧Token将作废</span>
<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>
<code>http://sa-oauth-server.com:8001/oauth2/userinfo?access_token={value}</code>
<br>
<h3>模式二隐藏式Implicit</h3>
<a href="http://sa-oauth-server.com:8001/oauth2/authorize?response_type=token&client_id=1001&redirect_uri=http://sa-oauth-client.com:8002/&scope=userinfo">
<button>隐藏式</button>
</a>
<span class="ps">越过授权码的步骤直接返回token到前端页面 格式http//:domain.com#token=xxxx-xxxx </span>
<code>http://sa-oauth-server.com:8001/oauth2/authorize?response_type=token&client_id=1001&redirect_uri=http://sa-oauth-client.com:8002/&scope=userinfo</code>
<br>
<h3>模式三密码式Password</h3>
<p class="pst">在下面输入Server端的用户名和密码使用密码式进行 OAuth2 授权登录</p>
账号:<input name="username">
密码:<input name="password">
<button onclick="passwordLogin()">登录</button>
<code>http://sa-oauth-server.com:8001/oauth2/token?grant_type=password&client_id={value}&username={value}&password={value}</code>
<br>
<h3>模式四凭证式Client Credentials</h3>
<p class="pst">以上三种模式获取的都是用户的 Access-Token代表用户对第三方应用的授权在OAuth2.0中还有一种针对 Client级别的授权
Client-Token代表应用自身的资源授权</p>
<p class="pst">Client-Token具有延迟作废特性在每次获取最新Client-Token的时候旧Client-Token不会立即过期而是作为Past-Token再次
储存起来资源请求方只要携带其中之一便可通过Token校验这种特性保证了在大量并发请求时不会出现“新旧Token交替造成的授权失效”
保证了服务的高可用</p>
<button onclick="getClientToken()">获取应用Client-Token</button>
<code>http://sa-oauth-server.com:8001/oauth2/client_token?grant_type=client_credentials&client_id={value}&client_secret={value}</code>
<br><br>
<span>更多资料请参考 Sa-Token 官方文档地址:</span>
<a href="http://sa-token.dev33.cn/">http://sa-token.dev33.cn/</a>
<div style="height: 200px;"></div>
</div>
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
<script src="https://www.layuicdn.com/layer-v3.1.1/layer.js"></script>
<script>window.jQuery || alert('当前页面CDN服务商已宕机请将所有js包更换为本地依赖')</script>
<script type="text/javascript">
// 根据code授权码进行登录
function doLogin(code) {
$.ajax({
url: '/codeLogin?code=' + code,
dataType: 'json',
success: function(res) {
console.log('返回:', res);
if(res.code == 200) {
setInfo(res.data);
layer.msg('登录成功!');
} else {
layer.msg(res.msg);
}
},
error: function(xhr, type, errorThrown){
return layer.alert("异常:" + JSON.stringify(xhr));
}
});
}
var code = getParam('code');
if(code) {
doLogin(code);
}
// 根据 Refresh-Token 去刷新 Access-Token
function refreshToken() {
var refreshToken = $('.refresh_token').text();
if(refreshToken == '') {
return layer.alert('您还没有获取 Refresh-Token ,请先授权登录');
}
$.ajax({
url: '/refresh?refreshToken=' + refreshToken,
dataType: 'json',
success: function(res) {
console.log('返回:', res);
if(res.code == 200) {
setInfo(res.data);
layer.msg('登录成功!');
} else {
layer.msg(res.msg);
}
},
error: function(xhr, type, errorThrown){
return layer.alert("异常:" + JSON.stringify(xhr));
}
});
}
// 模式三:密码式-授权登录
function passwordLogin() {
$.ajax({
url: '/passwordLogin',
data: {
username: $('[name=username]').val(),
password: $('[name=password]').val()
},
dataType: 'json',
success: function(res) {
console.log('返回:', res);
if(res.code == 200) {
setInfo(res.data);
layer.msg('登录成功!');
} else {
layer.msg(res.msg);
}
},
error: function(xhr, type, errorThrown){
return layer.alert("异常:" + JSON.stringify(xhr));
}
});
}
// 模式四:获取应用的 Client-Token
function getClientToken () {
$.ajax({
url: '/clientToken',
dataType: 'json',
success: function(res) {
console.log('返回:', res);
if(res.code == 200) {
setInfo(res.data);
layer.msg('获取成功!');
} else {
layer.msg(res.msg);
}
},
error: function(xhr, type, errorThrown){
return layer.alert("异常:" + JSON.stringify(xhr));
}
});
}
// 使用 Access-Token 置换资源: 获取账号昵称、头像、性别等信息
function getUserinfo() {
var accessToken = $('.access_token').text();
if(accessToken == '') {
return layer.alert('您还没有获取 Access-Token ,请先授权登录');
}
$.ajax({
url: '/getUserinfo',
data: {accessToken: accessToken},
dataType: 'json',
success: function(res) {
layer.alert(JSON.stringify(res.data));
},
error: function(xhr, type, errorThrown){
return layer.alert("异常:" + JSON.stringify(xhr));
}
});
}
// 注销
function logout() {
$.ajax({
url: '/logout',
dataType: 'json',
success: function(res) {
location.href = '/';
},
error: function(xhr, type, errorThrown){
return layer.alert("异常:" + JSON.stringify(xhr));
}
});
}
// 写入数据
function setInfo(info) {
console.log('info', info);
for (var key in info) {
$('.' + key).text(info[key]);
}
if($('.uid').text() == '') {
$('.uid').html('<b style="color: #E00;">未登录</b>')
}
}
setInfo({});
// 从url中查询到指定名称的参数值
function getParam(name, defaultValue){
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == name){return pair[1];}
}
return(defaultValue == undefined ? null : defaultValue);
}
</script>
</body>
</html>

View File

@@ -22,27 +22,27 @@
<dependencies>
<!-- springboot依赖 -->
<!-- SpringBoot依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- sa-token 权限认证, 在线文档http://sa-token.dev33.cn/ -->
<!-- Sa-Token 权限认证, 在线文档http://sa-token.dev33.cn/ -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>${sa-token-version}</version>
</dependency>
<!-- sa-token 实现 oauth2.0 -->
<!-- Sa-Token-OAuth2.0 模块 -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-oauth2</artifactId>
<version>${sa-token-version}</version>
</dependency>
<!-- sa-token整合redis (使用jackson序列化方式) -->
<!-- Sa-Token整合Redis (使用jackson序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
@@ -53,6 +53,19 @@
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- thymeleaf 视图引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 热刷新 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- ConfigurationProperties -->
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@@ -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端启动成功");
}
}

View File

@@ -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<String, Object> 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<String, Object> map = new LinkedHashMap<String, Object>();
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);
}
}

View File

@@ -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<String> 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<String> getClientScopeList(String clientId) {
return Arrays.asList("userinfo");
}
// 获取指定 LoginId 对指定 Client 已经授权过的所有 Scope
@Override
public List<String> 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;
}
/*
* 以上函数为开发时必须重写实现,其余函数可以按需重写
*/
// -------------- 其它需要重写的函数
}

View File

@@ -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

View File

@@ -0,0 +1,103 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sa-OAuth2-认证中心-确认授权页</title>
<style type="text/css">
body{background-color: #F5F5D5;}
*{margin: 0px; padding: 0px;}
.login-box{width: 400px; margin: 20vh auto; padding: 70px; border: 1px #000 solid;}
.login-box button{padding: 5px 15px; cursor: pointer; }
</style>
</head>
<body>
<div class="login-box">
<h2>Sa-OAuth2-认证中心-确认授权页</h2> <br>
<div>
<div><b>应用ID</b><span th:utext="${clientId}"></span></div>
<div><b>请求授权:</b><span th:utext="${scope}"></span></div>
<br><div>------------- 是否同意授权 -------------</div><br>
<div>
<button onclick="yes()">同意</button>
<button onclick="no()">拒绝</button>
</div>
</div>
</div>
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
<script src="https://www.layuicdn.com/layer-v3.1.1/layer.js"></script>
<script>window.jQuery || alert('当前页面CDN服务商已宕机请将所有js包更换为本地依赖')</script>
<script type="text/javascript">
// 同意授权
function yes() {
console.log('-----------');
$.ajax({
url: '/oauth2/doConfirm',
data: {
client_id: getParam('client_id'),
scope: getParam('scope')
},
dataType: 'json',
success: function(res) {
if(res.code == 200) {
layer.msg('授权成功!');
setTimeout(function() {
location.reload(true);
}, 800);
} else {
// 重定向至授权失败URL
layer.alert('授权失败!');
}
},
error: function(e) {
console.log('error');
}
});
}
// 拒绝授权
function no() {
var url = joinParam(getParam('redirect_uri'), "handle=refuse&msg=用户拒绝了授权");
location.href = url;
}
// 从url中查询到指定名称的参数值
function getParam(name, defaultValue){
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == name){return pair[1];}
}
return(defaultValue == undefined ? null : defaultValue);
}
// 在url上拼接上kv参数并返回
function joinParam(url, parameStr) {
if(parameStr == null || parameStr.length == 0) {
return url;
}
var index = url.indexOf('?');
// ? 不存在
if(index == -1) {
return url + '?' + parameStr;
}
// ? 是最后一位
if(index == url.length - 1) {
return url + parameStr;
}
// ? 是其中一位
if(index > -1 && index < url.length - 1) {
// 如果最后一位是 不是&, 且 parameStr 第一位不是 &, 就增送一个 &
if(url.lastIndexOf('&') != url.length - 1 && parameStrindexOf('&') != 0) {
return url + '&' + parameStr;
} else {
return url + parameStr;
}
}
}
</script>
</body>
</html>

View File

@@ -0,0 +1,54 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sa-OAuth2-认证中心-登录页</title>
<style type="text/css">
body{background-color: #F5F5D5;}
*{margin: 0px; padding: 0px;}
.login-box{width: 400px; margin: 20vh auto;}
.login-box input{line-height: 25px; margin-bottom: 10px;}
.login-box button{padding: 5px 15px; cursor: pointer; }
</style>
</head>
<body>
<div class="login-box">
<h2>Sa-OAuth2-认证中心-登录页</h2> <br>
账号:<input name="name" /> <br>
密码:<input name="pwd" type="password" /> <br>
<button onclick="doLogin()">登录</button>
<span style="color: #666;">(测试账号: sa 123456</span>
</div>
<script src="https://unpkg.zhimg.com/jquery@3.4.1/dist/jquery.min.js"></script>
<script src="https://www.layuicdn.com/layer-v3.1.1/layer.js"></script>
<script>window.jQuery || alert('当前页面CDN服务商已宕机请将所有js包更换为本地依赖')</script>
<script type="text/javascript">
// 登录方法
function doLogin() {
console.log('-----------');
$.ajax({
url: '/oauth2/doLogin',
data: {
name: $('[name=name]').val(),
pwd: $('[name=pwd]').val()
},
dataType: 'json',
success: function(res) {
if(res.code == 200) {
layer.msg('登录成功!');
setTimeout(function() {
location.reload(true);
}, 800);
} else {
layer.alert(res.msg);
}
},
error: function(e) {
console.log('error');
}
});
}
</script>
</body>
</html>

View File

@@ -79,7 +79,6 @@
<version>5.5.4</version>
</dependency> -->
</dependencies>

View File

@@ -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;