1
0
mirror of https://gitee.com/bookshelfplus/bookshelfplus synced 2025-09-01 22:53:29 +08:00
Code Issues Projects Releases Wiki Activity GitHub Gitee

Gitee授权登录成功

This commit is contained in:
2022-04-04 20:49:20 +08:00
parent 54911675a1
commit 5acd55c687
16 changed files with 229 additions and 63 deletions

View File

@@ -11,9 +11,6 @@ html,
body {
margin: 0;
font-family: bookshelfplusFont;
/* 字体加载前先隐藏,不然文字会闪一下 */
opacity: 0;
}
a,

View File

@@ -103,15 +103,32 @@ router.get('/dashboard/:group/:page', function (req, res) {
}, {
name: "我的收藏",
url: "/dashboard/user/myCollection"
}, {
name: "账号设置",
url: "/dashboard/user/myAccount"
}
];
}
var headText = req.params.group === "admin" ? "后台管理" : "用户中心";
var title = getPageTitle(headText);
var headSubTextArr = {
// 管理员
"UserManage": "用户管理",
"BookManage": "书籍管理",
"CategoryManage": "分类管理",
"Debug": "调试",
// 用户
"myBookshelf": "我的书架",
"myCollection": "我的收藏",
"myAccount": "账号设置"
};
// 仪表盘
if (req.params.page == "index") {
res.render(`dashboard/${req.params.group}/index`, {
title: getPageTitle(req.params.group === "admin" ? "后台管理" : "用户中心"),
headText: req.params.group === "admin" ? "后台管理" : "用户中心",
title: title,
headText: headText,
headSubTextArr: {},
links: navbarLinks,
group: req.params.group,
@@ -120,27 +137,32 @@ router.get('/dashboard/:group/:page', function (req, res) {
return;
}
// 后台管理 其他管理页面
// 后台管理 新增或者修改页面
if ((req.params.group === "admin" && ["UserManage", "BookManage", "CategoryManage", "Debug"].indexOf(req.params.page) > -1) ||
(req.params.group === "user" && ["myBookshelf", "myCollection"].indexOf(req.params.page) > -1)) {
res.render(`dashboard/${req.params.group}/manage`, {
title: getPageTitle(req.params.group === "admin" ? "后台管理" : "用户中心"),
headSubTextArr: {
// 管理员
"UserManage": "用户管理",
"BookManage": "书籍管理",
"CategoryManage": "分类管理",
"Debug": "调试",
// 用户
"myBookshelf": "我的书架",
"myCollection": "我的收藏",
},
title: title,
headSubTextArr: headSubTextArr,
links: navbarLinks,
group: req.params.group,
page: req.params.page,
});
return;
}
// 后台管理 其他管理页面
if ((req.params.group === "admin" && [].indexOf(req.params.page) > -1) ||
(req.params.group === "user" && ["myAccount"].indexOf(req.params.page) > -1)) {
res.render(`dashboard/${req.params.group}/${req.params.page}`, {
title: title,
headSubTextArr: headSubTextArr,
links: navbarLinks,
group: req.params.group,
page: req.params.page,
});
return;
}
throw new Error("404 Not Found");
});

View File

@@ -21,21 +21,41 @@
正在跳转中,请稍后...
</p>
<script>
getRequest("/third-party/callback/<%=platform%>" + location.search)
// 带 token 的为绑定第三方账号,不带 token 的为第三方登录
getRequest("/third-party/callback/<%=platform%>" + location.search + (localStorage.token ? ("&token=" + localStorage.token) : ""))
.then(function (response) {
var axiosData = response.data;
var status = axiosData.status;
var data = axiosData.data;
if (status === "success") {
console.log(data)
// 默认直接跳转 user 后台,如果是管理员则由 user 后台跳转
if(localStorage.token) {
// 绑定第三方账号
// 绑定第三方账号成功
alert("绑定成功");
location.href = "/dashboard/user/myAccount";
} else {
// 第三方登录成功
localStorage.setItem("token", data.token);
// alert("登录成功");
if(data.group === "ADMIN") {
localStorage.setItem("is_admin", "true");
window.location.href = "/dashboard/admin/index";
} else {
localStorage.setItem("is_admin", "false");
window.location.href = "/dashboard/user/index";
}
}
} else {
alert(`出错啦!${data.errMsg} (错误码: ${data.errCode}) `);
location.replace("/login");
}
}).catch(function (error) {
console.log(error);
document.getElementById("displayText").innerHTML="系统错误,请稍后再试!";
document.getElementById("displayText").style.color="red";
document.getElementById("displayText").innerHTML = "系统错误,请稍后再试!";
document.getElementById("displayText").style.color = "red";
});
</script>
</body>

View File

@@ -13,4 +13,8 @@
// API地址
const APIHOST = '<%= global.site.api.prefix %>';
axios.defaults.baseURL = APIHOST;
</script>
</script>
<style>
/* 字体加载前先隐藏,不然文字会闪一下 */
html, body { opacity: 0; }
</style>

View File

@@ -0,0 +1,20 @@
<button type="button" onclick="thirdPartyLogin('gitee')">Gitee</button>
<button type="button" onclick="thirdPartyLogin('qq')">QQ</button>
<script>
// 第三方授权登录逻辑
function thirdPartyLogin(type) {
getRequest("/third-party/login", { platform: type })
.then(function (response) {
var axiosData = response.data;
var status = axiosData.status;
var data = axiosData.data;
if (status === "success") {
location.href = data;
} else {
alert(`出错啦!${data.errMsg} (错误码: ${data.errCode}) `);
}
}).catch(function (error) {
console.log(error);
});
}
</script>

View File

@@ -52,7 +52,7 @@
if(linkRoute.length > 2) {
var linkGroup = linkRoute[linkRoute.length-2];
var linkPage = linkRoute[linkRoute.length-1];
console.log(element, linkGroup, linkPage);
// console.log(element, linkGroup, linkPage);
if(page == linkPage) {
$(element).addClass("active");
}

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<%- include("../component/header.html"); %>
<%- include("../component/header-user.html"); %>
</head>
<body>
<%- include("../component/navbar.html"); %>
<main class="main">
<h1><%= headSubTextArr[page] || page %></h1>
<div id="container">
<div>
第三方账号绑定:
<%- include("../../component/third-party-login-button.html"); %>
</div>
</div>
</main>
<%- include("../component/footer-user.html"); %>
</body>
</html>

View File

@@ -72,9 +72,7 @@
</div>
<button class="btn-submit">登录</button>
<p>
快捷登录:
<button type="button" onclick="thirdPartyLogin('gitee')">Gitee</button>
<button type="button" onclick="thirdPartyLogin('qq')">QQ</button>
快捷登录:<%- include("./component/third-party-login-button.html"); %>
</p>
<p>
<a href="/register">注册</a>
@@ -130,23 +128,5 @@
});
});
</script>
<script>
// 第三方授权登录逻辑
function thirdPartyLogin(type) {
getRequest("/third-party/login", { platform: type })
.then(function (response) {
var axiosData = response.data;
var status = axiosData.status;
var data = axiosData.data;
if (status === "success") {
location.href = data;
} else {
alert(`出错啦!${data.errMsg} (错误码: ${data.errCode}) `);
}
}).catch(function (error) {
console.log(error);
});
}
</script>
</body>
</html>

View File

@@ -216,7 +216,7 @@ CREATE TABLE `user_info` (
`nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`group` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`phone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`weixin_third_party_auth_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
`qq_third_party_auth_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',

View File

@@ -5,6 +5,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import plus.bookshelf.Common.Enum.plus.bookshelf.TencentCloud.COS.CosProperties;
@@ -16,6 +17,7 @@ import plus.bookshelf.Common.TencentCloud.COS.GeneratePresignatureUrl;
@SpringBootApplication(scanBasePackages = {"plus.bookshelf"})
@RestController
@MapperScan("plus.bookshelf.Dao.Mapper")
// @EnableTransactionManagement // 引入事务管理
public class App {
public static void main(String[] args) {
System.out.println("Dreams remain daydreams until they are put into action. \n" +

View File

@@ -16,8 +16,8 @@ public enum BusinessErrorCode implements CommonError {
OPERATION_NOT_ALLOWED(30001, "用户没有此操作的权限"),
// 40000开头为第三方登录相关错误定义
THIRD_PARTY_LOGIN_FAIL(40001, "第三方登录失败");
THIRD_PARTY_LOGIN_FAIL(40001, "第三方登录失败"),
THIRD_PARTY_ACCOUNT_ALREADY_BOUND(40002, "该账号已被其他账号绑定");
private BusinessErrorCode(int errCode, String errMsg) {

View File

@@ -53,11 +53,11 @@ public class ThirdPartyController extends BaseController {
return CommonReturnType.create(authorizeUrl);
}
@ApiOperation(value = "快捷登录回调函数", notes = "传入 code 值,进行登录")
@ApiOperation(value = "快捷登录回调函数", notes = "如果传入 token 那么就是绑定第三方账号到当前登录账号,否则就是通过第三方授权登录")
@RequestMapping(value = "callback/{platform}", method = {RequestMethod.GET})
@ResponseBody
public CommonReturnType callback(@PathVariable("platform") String platform,
// @RequestParam Map<String,String> params,
@RequestParam(value = "token", required = false) String token,
AuthCallback callback) throws BusinessException {
AuthRequest authRequest = getAuthRequest(platform);
AuthResponse authResponse;
@@ -67,15 +67,21 @@ public class ThirdPartyController extends BaseController {
// [ERROR] - Failed to login with oauth authorization.
throw new BusinessException(BusinessErrorCode.THIRD_PARTY_LOGIN_FAIL, "第三方登录失败");
}
if (token == null || token.isEmpty()) {
// 通过第三方授权登录
UserModel userModel = thirdPartyUserService.loginCallback(authResponse);
UserVO userVO = UserController.convertFromService(userModel);
UserModel userModel = thirdPartyUserService.loginCallback(authResponse);
UserVO userVO = UserController.convertFromService(userModel);
if (userModel != null) {
String token = onLogin(userModel);
userVO.setToken(token); // token 仅在用户登录时传一次,后面获取用户状态接口中不重复返回 token 信息
if (userModel != null) {
String userLoginToken = onLogin(userModel);
userVO.setToken(userLoginToken); // token 仅在用户登录时传一次,后面获取用户状态接口中不重复返回 token 信息
}
return CommonReturnType.create(userVO);
} else {
// 绑定第三方账号到当前登录账号
Boolean isSuccess = thirdPartyUserService.bindThirdPartAccountCallback(authResponse, token);
return CommonReturnType.create(isSuccess);
}
return CommonReturnType.create(userVO);
}
// 创建授权request

View File

@@ -55,10 +55,16 @@ public interface ThirdPartyUserDOMapper {
int updateByPrimaryKey(ThirdPartyUserDO record);
/**
* 通过 uuid + source 查询第三方登录的用户信息limit 1
* 通过 uuid + source 查询第三方登录的用户信息
* @param uuid
* @param source
* @return
*/
ThirdPartyUserDO selectByUuidAndSource(String uuid, String source);
/**
* 获取上一步插入数据的主键id
* @return
*/
Integer getLastInsertId();
}

View File

@@ -5,6 +5,7 @@ import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import plus.bookshelf.Common.Error.BusinessErrorCode;
@@ -15,8 +16,9 @@ import plus.bookshelf.Dao.Mapper.ThirdPartyUserAuthDOMapper;
import plus.bookshelf.Dao.Mapper.ThirdPartyUserDOMapper;
import plus.bookshelf.Service.Model.UserModel;
import plus.bookshelf.Service.Service.ThirdPartyUserService;
import plus.bookshelf.Service.Service.UserService;
import java.util.Map;
import javax.annotation.Resource;
@Service
public class ThirdPartyUserServiceImpl implements ThirdPartyUserService {
@@ -30,6 +32,16 @@ public class ThirdPartyUserServiceImpl implements ThirdPartyUserService {
@Autowired
UserServiceImpl userService;
@Autowired
RedisTemplate redisTemplate;
/**
* 第三方登录
*
* @param authResponse
* @return
* @throws BusinessException
*/
@Override
@Transactional
public UserModel loginCallback(AuthResponse authResponse) throws BusinessException {
@@ -53,14 +65,16 @@ public class ThirdPartyUserServiceImpl implements ThirdPartyUserService {
if (existThirdPartyUserDO != null) {
// 之前已经授权登录过
// 更新数据库中的第三方登录信息
currentThirdPartyUserDO.setId(existThirdPartyUserDO.getId());
thirdPartyUserDOMapper.updateByPrimaryKeySelective(currentThirdPartyUserDO);
// 检查第三方账号有无绑定到系统账号
ThirdPartyUserAuthDO thirdPartyUserAuthDO = thirdPartyUserAuthDOMapper.selectByThirdPartyUserId(currentThirdPartyUserDO.getId());
ThirdPartyUserAuthDO thirdPartyUserAuthDO = thirdPartyUserAuthDOMapper.selectByThirdPartyUserId(existThirdPartyUserDO.getId());
if (thirdPartyUserAuthDO != null) {
// 已经绑定到系统账号
// 更新数据库中的第三方登录信息
currentThirdPartyUserDO.setId(existThirdPartyUserDO.getId());
thirdPartyUserDOMapper.updateByPrimaryKeySelective(currentThirdPartyUserDO);
// 取得用户信息,并登录
Integer userId = thirdPartyUserAuthDO.getUserId();
UserModel userModel = userService.getUserById(userId);
@@ -71,8 +85,6 @@ public class ThirdPartyUserServiceImpl implements ThirdPartyUserService {
}
} else {
// 之前未授权登录过
// 将新的用户信息插入到数据库
thirdPartyUserDOMapper.insertSelective(currentThirdPartyUserDO);
throw new BusinessException(BusinessErrorCode.PARAMETER_VALIDATION_ERROR, "第三方登录失败,该第三方账号未绑定到系统账号,请先绑定");
}
} else {
@@ -81,6 +93,70 @@ public class ThirdPartyUserServiceImpl implements ThirdPartyUserService {
}
}
@Override
@Transactional
public Boolean bindThirdPartAccountCallback(AuthResponse authResponse, String token) throws BusinessException {
int code = authResponse.getCode();
if (code == 5008) {
// 第三方平台拒绝授权
throw new BusinessException(BusinessErrorCode.PARAMETER_VALIDATION_ERROR, "绑定失败,用户已取消第三方授权或第三方平台拒绝授权");
} else if (code == 5009) {
// 授权过期
throw new BusinessException(BusinessErrorCode.PARAMETER_VALIDATION_ERROR, "第三方授权过期,请尝试重新绑定");
}
if (code == 2000) {
// 回调成功
// 将回调结果转换为 Data Object
ThirdPartyUserDO currentThirdPartyUserDO = getThirdPartyUserDOFromAuthData(authResponse.getData());
// 根据 uuid + source 唯一确定一个平台的用户 refer: https://justauth.wiki/features/integrate-existing-systems/
String uuid = currentThirdPartyUserDO.getUuid();
String source = currentThirdPartyUserDO.getSource();
ThirdPartyUserDO existThirdPartyUserDO = thirdPartyUserDOMapper.selectByUuidAndSource(uuid, source);
if (existThirdPartyUserDO == null) {
// 之前未授权过
// 获取当前登录用户信息
UserModel userModel = userService.getUserByToken(redisTemplate, token);
if (userModel == null) {
throw new BusinessException(BusinessErrorCode.PARAMETER_VALIDATION_ERROR, "绑定失败,用户未登录");
}
// 将用户账号与第三方账号信息插入到数据库
int affectRows = thirdPartyUserDOMapper.insertSelective(currentThirdPartyUserDO);
// 判断是否插入成功
if (affectRows > 0) {
// 用户第三方账号保存到数据库表中的主键id
Integer lastInsertId = thirdPartyUserDOMapper.getLastInsertId();
// 在 Auth 表中建立 用户 和第三方授权的联系
ThirdPartyUserAuthDO thirdPartyUserAuthDO = new ThirdPartyUserAuthDO();
thirdPartyUserAuthDO.setThirdPartyUserId(lastInsertId);
thirdPartyUserAuthDO.setUserId(userModel.getId());
int affectRows2 = thirdPartyUserAuthDOMapper.insert(thirdPartyUserAuthDO);
// 判断是否插入成功
if (affectRows2 > 0) {
// 绑定成功
return true;
} else {
throw new BusinessException(BusinessErrorCode.UNKNOWN_ERROR, "绑定失败,系统错误");
}
} else {
// 第三方账号信息插入失败
throw new BusinessException(BusinessErrorCode.UNKNOWN_ERROR, "绑定失败,系统错误");
}
} else {
// 之前已经授权过
throw new BusinessException(BusinessErrorCode.THIRD_PARTY_ACCOUNT_ALREADY_BOUND, "绑定失败,该账号已被其他账号绑定");
}
} else {
// 未知错误
throw new BusinessException(BusinessErrorCode.UNKNOWN_ERROR, "未知错误,绑定失败");
}
}
private ThirdPartyUserDO getThirdPartyUserDOFromAuthData(Object authData) {
AuthUser data = (AuthUser) authData;
String uuid = data.getUuid();

View File

@@ -15,4 +15,14 @@ public interface ThirdPartyUserService {
*/
@Transactional
UserModel loginCallback(AuthResponse authResponse) throws BusinessException;
/**
* 个人账号中心绑定第三方账号回调函数
* @param authResponse
* @throws BusinessException
* @return
*/
@Transactional
Boolean bindThirdPartAccountCallback(AuthResponse authResponse, String token) throws BusinessException;
}

View File

@@ -283,4 +283,7 @@
from third_party_user_info
where uuid = #{uuid} and `source` = #{source}
</select>
<select id="getLastInsertId" resultType="java.lang.Integer">
SELECT LAST_INSERT_ID();
</select>
</mapper>