集成钉钉登录

This commit is contained in:
jinqiming 2021-05-08 17:51:20 +08:00
parent 8ca13edc9b
commit 9b31643e83
22 changed files with 857 additions and 51 deletions

View File

@ -1,26 +1,47 @@
package com.snow.web.controller.dingtalk;
import com.alibaba.fastjson.JSONObject;
import com.dingtalk.api.response.OapiSnsGetuserinfoBycodeResponse;
import com.dingtalk.api.response.OapiUserGetbyunionidResponse;
import com.dingtalk.api.response.OapiV2UserGetResponse;
import com.snow.common.core.controller.BaseController;
import com.snow.common.core.domain.AjaxResult;
import com.snow.common.utils.AuthUtils;
import com.snow.common.utils.ServletUtils;
import com.snow.common.utils.StringUtils;
import com.snow.dingtalk.service.UserService;
import com.snow.framework.shiro.auth.LoginType;
import com.snow.framework.shiro.auth.UserToken;
import com.snow.framework.util.ShiroUtils;
import com.snow.system.domain.SysAuthUser;
import com.snow.system.domain.SysSocialUser;
import com.snow.system.domain.SysUser;
import com.snow.system.mapper.SysUserMapper;
import com.snow.system.service.ISysConfigService;
import com.snow.system.service.impl.SysSocialUserServiceImpl;
import com.snow.system.service.impl.SysUserServiceImpl;
import me.zhyd.oauth.cache.AuthDefaultStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthDingTalkRequest;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @program: snow
@ -34,58 +55,118 @@ public class ThirdOauthController extends BaseController {
@Autowired
private ISysConfigService iSysConfigService;
@Autowired
private UserService userService;
@Autowired
private SysUserServiceImpl sysUserService;
@Resource
private SysUserMapper userMapper;
@Autowired
private SysSocialUserServiceImpl sysSocialUserService;
/**
* 跳转钉钉授权页面
* 认证授权
*
* @param source
* @throws IOException
*/
@GetMapping("/toDingPage")
public String toDingPage()
@GetMapping("/toDingPage/{source}")
@ResponseBody
public void renderAuth(@PathVariable("source") String source) throws IOException
{
String appId= iSysConfigService.selectConfigByKey("ding.login.appid");
StringBuilder url=new StringBuilder("https://oapi.dingtalk.com/connect/qrconnect?appid=");
url.append(appId).append("&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=");
url.append("http://workflow.vaiwan.com/third/oauth/dingTalkLogin");
return redirect(url.toString());
String appSecret= iSysConfigService.selectConfigByKey("ding.login.appSecret");
String url="http://workflow.vaiwan.com/third/oauth/dingTalkLogin";
AuthRequest authRequest = new AuthDingTalkRequest(AuthConfig.builder()
.clientId(appId)
.clientSecret(appSecret)
.redirectUri(url)
.build());
String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
ServletUtils.getResponse().sendRedirect(authorizeUrl);
}
@RequestMapping("/dingTalkLogin")
@ResponseBody
public AjaxResult dingTalkLogin(String code)
/**
* 回调结果
*/
@SuppressWarnings("unchecked")
@GetMapping("/auth/callback/{source}")
public Object callbackAuth(@PathVariable("source") String source, AuthCallback callback, HttpServletRequest request)
{
OapiSnsGetuserinfoBycodeResponse.UserInfo userInfoByCode = userService.getUserInfoByCode(code);
OapiUserGetbyunionidResponse.UserGetByUnionIdResponse userByUnionId = userService.getUserByUnionId(userInfoByCode.getUnionid());
SysUser sysUser = sysUserService.selectUserByDingUserId(userByUnionId.getUserid());
SysSocialUser sysSocialUser=new SysSocialUser();
sysSocialUser.setCode(code);
sysSocialUser.setOpenId(userInfoByCode.getOpenid());
sysSocialUser.setUnionId(userInfoByCode.getUnionid());
sysSocialUser.setSource("DING_TALK");
sysSocialUser.setUserId(sysUser.getUserId());
sysSocialUser.setAccessToken(userInfoByCode.getUnionid());
//sysSocialUserService.deleteSysSocialUserById(1L);
if(StringUtils.isNotNull(sysUser)){
//todo 登录系统
UsernamePasswordToken token = new UsernamePasswordToken(sysUser.getPhonenumber(), sysSocialUser.getUnionId(), false,"2");
Subject subject = SecurityUtils.getSubject();
}else {
return AjaxResult.error("非企业内用户不允许扫码登录");
if (StringUtils.isEmpty(source))
{
return new ModelAndView("error/unauth");
}
String appId= iSysConfigService.selectConfigByKey("ding.login.appid");
String appSecret= iSysConfigService.selectConfigByKey("ding.login.appSecret");
String url="http://workflow.vaiwan.com/third/oauth/dingTalkLogin";
AuthRequest authRequest = new AuthDingTalkRequest(AuthConfig.builder()
.clientId(appId)
.clientSecret(appSecret)
.redirectUri(url)
.build());
AuthResponse<AuthUser> response = authRequest.login(callback);
if (response.ok())
{
if (SecurityUtils.getSubject() != null && SecurityUtils.getSubject().getPrincipal() != null)
{
SysUser user = userMapper.selectAuthUserByUuid(source + response.getData().getUuid());
if (StringUtils.isNotNull(user))
{
return redirect("/index");
}
// 若已经登录则直接绑定系统账号
SysAuthUser authUser = new SysAuthUser();
authUser.setAvatar(response.getData().getAvatar());
authUser.setUuid(source + response.getData().getUuid());
authUser.setUserId(ShiroUtils.getUserId());
authUser.setUserName(response.getData().getNickname());
authUser.setLoginName(response.getData().getUsername());
authUser.setEmail(response.getData().getEmail());
authUser.setSource(source);
userMapper.insertAuthUser(authUser);
return redirect("/index");
}
SysUser user = userMapper.selectAuthUserByUuid(source + response.getData().getUuid());
if (StringUtils.isNotNull(user))
{
Subject subject = SecurityUtils.getSubject();
UserToken token = new UserToken(user.getLoginName(), LoginType.NOPASSWD);
subject.login(token);
return redirect("/index");
}
else
{
return new ModelAndView("error/bind");
}
}
return new ModelAndView("error/404");
}
/**
* 检查是否授权
*/
@PostMapping("/auth/checkAuthUser")
@ResponseBody
public AjaxResult checkAuthUser(SysAuthUser authUser)
{
Long userId = ShiroUtils.getUserId();
String source = authUser.getSource();
if (userMapper.checkAuthUser(userId, source) > 0)
{
return error(source + "平台账号已经绑定");
}
return AjaxResult.success();
}
/**
* 取消授权
*/
@PostMapping("/auth/unlock")
@ResponseBody
public AjaxResult unlockAuth(SysAuthUser authUser)
{
return toAjax(userMapper.deleteAuthUser(authUser.getAuthId()));
}
}

View File

@ -2,6 +2,9 @@ package com.snow.web.controller.system;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.snow.framework.shiro.auth.LoginType;
import com.snow.framework.shiro.auth.UserToken;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
@ -39,7 +42,8 @@ public class SysLoginController extends BaseController
@ResponseBody
public AjaxResult ajaxLogin(String username, String password, Boolean rememberMe)
{
UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe,"1");
//UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe,"1");
UserToken token = new UserToken(username, password, LoginType.PASSWORD, rememberMe);
Subject subject = SecurityUtils.getSubject();
try
{

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M512 1024C229.234 1024 0 794.766 0 512S229.234 0 512 0s512 229.234 512 512-229.234 512-512 512z m259.157-568.889l-290.759 0.014c-13.966 0-25.287 11.321-25.287 25.273l-0.028 63.218c0 13.966 11.306 25.287 25.273 25.287H657.38c13.966 0 25.287 11.307 25.287 25.273v12.644a75.847 75.847 0 0 1-75.847 75.847H366.606a25.287 25.287 0 0 1-25.287-25.273v-240.2a75.847 75.847 0 0 1 75.847-75.846l353.92-0.015c13.966 0 25.273-11.306 25.287-25.273l0.071-63.189c0-13.966-11.306-25.287-25.272-25.301l-353.992 0.014c-104.718-0.014-189.624 84.892-189.624 189.61v353.963c0 13.967 11.32 25.287 25.287 25.287h372.935c94.265 0 170.666-76.401 170.666-170.666v-145.38c0-13.952-11.32-25.273-25.287-25.273z" fill="#d81e06" /></svg>

After

Width:  |  Height:  |  Size: 973 B

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M0 520.886c0-69.368 13.51-135.697 40.498-199.02 26.987-63.323 63.322-117.826 109.006-163.51 45.65-45.65 100.154-81.985 163.51-109.006A502.289 502.289 0 0 1 512 8.92c69.335 0 135.663 13.477 198.986 40.497 63.356 26.988 117.86 63.323 163.51 109.007 45.684 45.65 82.02 100.154 109.006 163.51A502.289 502.289 0 0 1 1024 520.852c0 111.318-32.504 211.472-97.511 300.494-64.975 88.989-148.48 150.825-250.484 185.476-5.351 0-9.348-0.99-11.99-2.973-2.676-1.982-4.196-3.997-4.526-6.012a59.458 59.458 0 0 1-0.495-8.984 7.663 7.663 0 0 1-0.991-3.006v-128.99c0-40.63-14.336-75.314-43.008-103.986 76.667-13.345 134.011-41.819 171.999-85.487 37.987-43.669 57.013-96.52 57.013-158.522 0-58.005-18.663-108.346-56.022-150.99 13.345-42.678 11-87.668-6.97-135.003-18.697-1.322-39.011 1.85-61.01 9.513-22 7.663-38.318 14.831-49.02 21.47-10.637 6.673-20.316 13.016-28.97 19.027-38.68-10.669-81.854-16.02-129.486-16.02-47.7 0-90.509 5.351-128.529 16.02-7.333-5.35-15.855-11.164-25.5-17.507-9.68-6.342-26.493-14.005-50.507-22.99-23.982-9.018-45.65-12.85-65.008-11.495-18.663 47.996-20.645 93.646-5.979 136.984-36.665 42.678-54.998 92.986-54.998 150.99 0 62.002 18.663 114.689 55.99 157.994 37.326 43.339 94.67 72.01 171.998 86.016a142.303 142.303 0 0 0-39.969 70.029c-56.683 13.972-96.355 3.963-119.015-30.06-42.017-61.308-79.674-83.307-113.003-65.965-4.69 4.657-3.997 9.48 1.982 14.501 6.012 4.988 14.996 11.66 27.02 19.985 11.99 8.357 20.976 17.507 26.987 27.515 0.661 1.322 2.51 6.177 5.517 14.502a831.917 831.917 0 0 0 8.985 23.981c2.973 7.663 8.654 16.186 17.011 25.5 8.324 9.349 18.003 17.178 29.003 23.52 11 6.309 26.161 11 45.485 14.006 19.324 2.972 41.323 3.138 65.998 0.495v100.484c0 0.991-0.165 2.643-0.495 5.021-0.33 2.312-0.991 3.964-1.982 4.955-0.991 1.024-2.345 2.015-4.03 3.039a12.52 12.52 0 0 1-6.474 1.486c-2.676 0-6.012-0.33-10.009-0.99-101.343-35.345-183.825-97.182-247.51-185.51C31.842 731.037 0 631.577 0 520.92z" /></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M512 0C229.2224 0 0 229.21728 0 512s229.2224 512 512 512 512-229.21728 512-512S794.7776 0 512 0z m281.89696 671.57504c-13.22496 12.34432-35.92192-1.10592-57.74336-31.29856a330.44992 330.44992 0 0 1-36.1472 70.30784c30.85312 11.01824 50.69312 28.2112 50.69312 47.61088 0 33.50016-59.2896 60.61056-132.45952 60.61056-43.4176 0-81.77152-9.47712-106.01472-24.2432-24.02304 14.76608-62.592 24.2432-106.01472 24.2432-73.17504 0-132.45952-27.1104-132.45952-60.61056 0-19.1744 19.83488-36.58752 50.69312-47.61088-14.54592-21.16096-26.66496-44.74368-36.14208-70.30784-21.82144 29.97248-44.52352 43.63776-57.74336 31.29856-18.0736-16.9728-11.24352-76.92288 15.64672-133.7856 6.1696-13.0048 12.78464-24.68352 19.61472-34.82112 3.74784-165.30432 112.62976-297.98912 246.19008-297.98912h0.44032c133.56544 0 242.44736 132.45952 246.19008 297.98912 6.8352 10.1376 13.44 21.82144 19.61472 34.82112 26.65984 56.86272 33.7152 116.8128 15.6416 133.7856z" fill="#1296db" /></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1615369931580" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1299" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M512 2C230.6 2 2 230.6 2 512s228.6 510 510 510 510-228.6 510-510S793.3 2 512 2z m-93.5 644.3c-24.6 0-49.3-4.3-71.4-10.2-1.7 0-4.3-1.7-4.3-1.7l-55.3 32.3c-14.4 8.5-24.7 1.7-20.4-14.4 0 0 14.4-59.5 11.9-59.5-52.7-34.9-77.4-79.9-77.4-141.1 4.3-102.8 94.4-184.5 216.8-184.5 112.2 0 204 69.7 220.2 161.5h-10.2c-112.2 0-204 77.4-204 173.4 0 16.1 1.7 30.6 6 45h-11.9v-0.8z m328.1 79.9l16.1 32.3c8.5 14.5 1.7 20.4-11.9 11.9 0 0-40.8-24.7-42.5-22.1-18.7 6-59.5 11.9-79.9 11.9-103.7 0-189.6-71.4-189.6-159s85.9-159 189.6-159S818 513.7 818 601.3c0 51.8-26.3 94.3-71.4 124.9zM353 363.3c-8.5-5.1-19.5-5.1-28.9 0-8.5 5.1-14.4 14.4-14.4 24.6s5.1 19.6 14.4 24.7c8.5 5.1 19.6 5.1 28.9 0 8.5-5.1 14.5-14.5 14.5-24.7s-6-19.6-14.5-24.6zM510.3 416c16.2 0 28.9-12.8 28.9-28.9 0-10.2-5.1-19.5-14.4-24.6-8.5-5.1-19.6-5.1-28.9 0-9.3 5.1-14.4 14.4-14.4 24.6-0.1 16.1 12.6 28.9 28.8 28.9z m187 100.3c-13.6 0-24.6 11-24.6 24.6s11 24.6 24.6 24.6 24.7-11 24.7-24.6-11.1-24.6-24.7-24.6z m-134.3 0c-13.6 0-24.6 11-24.6 24.6s11 24.6 24.6 24.6 24.6-11 24.6-24.6-11-24.6-24.6-24.6z" fill="#08BA06" p-id="1300"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RuoYi - 403</title>
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet"/>
<link th:href="@{/css/animate.css}" rel="stylesheet"/>
<link th:href="@{/css/style.css}" rel="stylesheet"/>
</head>
<body class="gray-bg">
<div class="middle-box text-center animated fadeInDown">
<h1>500</h1>
<h3 class="font-bold">没有绑定注册用户!</h3>
<div class="error-desc">
对不起,您没有绑定注册用户,请先注册后在个人中心绑定第三方授权信息!您可以返回登录页面
<a href="javascript:index()" class="btn btn-outline btn-primary btn-xs">返回登录页</a>
</div>
</div>
<script th:inline="javascript">
var ctx = [[@{/}]];
function index() {
window.top.location = ctx + "login";
}
</script>
</body>
</html>

View File

@ -15,7 +15,7 @@
<!-- 避免IE使用兼容模式 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="shortcut icon" href="../static/favicon.ico" th:href="@{favicon.ico}"/>
<style type="text/css">label.error { position:inherit; }</style>
<style type="text/css">label.error{position:inherit}.ui.horizontal.divider::before,.ui.horizontal.divider::after{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABaAAAAACCAYAAACuTHuKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyFpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIChXaW5kb3dzKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo1OThBRDY4OUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo1OThBRDY4QUNDMTYxMUU0OUE3NUVGOEJDMzMzMjE2NyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjU5OEFENjg3Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjU5OEFENjg4Q0MxNjExRTQ5QTc1RUY4QkMzMzMyMTY3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+VU513gAAADVJREFUeNrs0DENACAQBDBIWLGBJQby/mUcJn5sJXQmOQMAAAAAAJqt+2prAAAAAACg2xdgANk6BEVuJgyMAAAAAElFTkSuQmCC")}.ui.horizontal.divider::after{background-position:left 1em top 50%}.ui.horizontal.divider::before,.ui.horizontal.divider::after{content:'';display:table-cell;position:relative;top:50%;width:50%;background-repeat:no-repeat}.ui.horizontal.divider::before{background-position:right 1em top 50%}.ui.horizontal.list{display:inline-block;font-size:0}ul.ui.list:last-child,ol.ui.list:last-child,.ui.list:last-child{margin-bottom:0;padding-bottom:0}ul.ui.list,ol.ui.list,.ui.list{list-style-type:none;margin:1em 0;padding:0}.login-oauth__list{display:-webkit-box!important;display:-ms-flexbox!important;display:flex!important;-webkit-box-pack:center!important;-ms-flex-pack:center!important;justify-content:center!important}.login-login-oauth__container{margin-top:20px}.ui.horizontal.login-login__oauth-title{font-weight:normal}.ui.divider{margin:1.4rem 0rem;line-height:1;height:0;font-weight:bold;text-transform:uppercase;letter-spacing:.05em;color:rgba(0,0,0,0.85);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:rgba(0,0,0,0)}.ui.horizontal.divider{display:table;white-space:nowrap;height:auto;margin:'';line-height:1;text-align:center}.ui.horizontal.list>.item:first-child,.ui.horizontal.list>.item:last-child{padding-top:.3em;padding-bottom:.3em}.ui.horizontal.list>.item:first-child{margin-left:0!important;padding-left:0!important}.login-oauth__list>.item{margin-left:40px!important}.ui.list .item a>img{width:32px;height:32px}</style>
<script>
if(window.top!==window.self){alert('未登录或登录超时。请重新登录');window.top.location=window.location};
</script>
@ -61,6 +61,28 @@
<input type="checkbox" id="rememberme" name="rememberme"> <label for="rememberme">记住我</label>
</div>
<button class="btn btn-success btn-block" id="btnSubmit" data-loading="正在验证登录,请稍后...">登录</button>
<div class="login-login-oauth__container">
<div class="ui horizontal divider login-login__oauth-title">
<span class="text-muted">其他方式登录</span>
</div>
<div class="ui horizontal list login-oauth__list">
<div class="item" title="使用 钉钉 账号授权登录">
<a th:href="@{/third/oauth/toDingPage/dingtalk}">
<img src="../static/img/dingtalk.png" th:src="@{/img/dingtalk.png}"/>
</a>
</div>
<div class="item" title="功能开发中...">
<a href="#">
<img src="../static/img/weixin.svg" th:src="@{/img/weixin.svg}"/>
</a>
</div>
<div class="item" title="功能开发中...">
<a href="#">
<img src="../static/img/qq.svg" th:src="@{/img/qq.svg}"/>
</a>
</div>
</div>
</div>
<!-- <a class="btn btn-success btn-block" th:href="@{/third/oauth/toDingPage}" data-loading="正在验证登录,请稍后...">钉钉登录</a>-->
</form>
</div>

View File

@ -55,6 +55,7 @@
<ul class="nav nav-tabs">
<li class="active"><a href="#user_info" data-toggle="tab" aria-expanded="true">基本资料</a></li>
<li><a href="#modify_password" data-toggle="tab" aria-expanded="false">修改密码</a></li>
<li><a href="#third_party_binding" data-toggle="tab" aria-expanded="false">第三方帐号绑定</a></li>
</ul>
<div class="tab-content">
<!--用户信息-->
@ -131,6 +132,44 @@
</div>
</form>
</div>
<!--绑定第三方账号登录-->
<div class="tab-pane" id="third_party_binding">
<div class="form-horizontal">
<div class="form-group">
<div class="col-sm-12">
<div class="col-sm-12 select-table table-striped">
<table id="bootstrap-table"></table>
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-12">
<div id="git-user-binding">
<h4 class="provider-desc">你可以绑定以下第三方帐号用于DingFlow系统</h4>
<div id="authlist" class="user-bind">
<a class="third-app" href="#" onclick="authUrl('gitee');" title="使用 Gitee 账号授权登录">
<div class="git-other-login-icon"><img th:src="@{/img/gitee.svg}"></div>
<span class="app-name">Gitee</span></a>
<a class="third-app" href="#" onclick="authUrl('github');" title="使用 GitHub 账号授权登录">
<div class="git-other-login-icon"><img th:src="@{/img/github.svg}"></div>
<span class="app-name">Github</span></a>
<a class="third-app" href="#" title="功能开发中...">
<div class="git-other-login-icon"><img th:src="@{/img/weixin.svg}"></div>
<span class="app-name">WeiXin</span></a>
<a class="third-app" href="#" title="功能开发中...">
<div class="git-other-login-icon"><img th:src="@{/img/qq.svg}"></div>
<span class="app-name">QQ</span></a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@ -287,6 +326,91 @@
$.operate.saveModal(ctx + "system/user/profile/resetPwd", $('#form-user-resetPwd').serialize());
}
}
// 第三方授权列表
$(function() {
var auths = [[${auths}]]
var options = {
data: auths,
sidePagination: "client",
showSearch: false,
showRefresh: false,
showToggle: false,
showColumns: false,
pagination: false,
columns: [{
title: "序号",
formatter: function (value, row, index) {
return $.table.serialNumber(index);
}
},
{
field: 'source',
title: '绑定账号平台'
},
{
field: 'avatar',
title: '头像',
formatter: function(value, row, index) {
return $.table.imageView(value);
}
},
{
field: 'loginName',
title: '账号'
},
{
field: 'createTime',
title: '绑定时间'
},
{
title: '操作',
align: 'center',
formatter: function(value, row, index) {
var actions = [];
actions.push('<a class="btn btn-danger btn-xs" href="javascript:void(0)" onclick="unlockAuth(this, \'' + row.authId + '\', \'' + row.source + '\')"><i class="fa fa-remove"></i>解除绑定</a>');
return actions.join('');
}
}]
};
$.table.init(options);
});
// 解除绑定
function unlockAuth(obj, authId, source) {
$.modal.confirm("您确定要解除" + source + "的账号绑定吗?", function() {
$.ajax({
type: "post",
url: ctx + "auth/unlock",
data: { "authId": authId },
success: function(r) {
if (r.code == web_status.SUCCESS) {
$(obj).parents("tr").remove();
$.modal.msgSuccess("解绑成功");
} else {
$.modal.msgError(r.msg);
}
}
});
});
}
// 第三方登录授权
function authUrl(source) {
$.ajax({
type: "post",
url: ctx + "auth/checkAuthUser",
data: { "source": source },
success: function(r) {
if (r.code == web_status.SUCCESS) {
var url = ctx + "auth/" + source;
top.location.href =url;
} else {
$.modal.msgError(r.msg);
}
}
});
}
</script>
</body>
</html>

View File

@ -136,6 +136,19 @@
<artifactId>jsqlparser</artifactId>
<version>1.4</version>
</dependency>
<!-- 第三方授权登录 -->
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>1.15.6</version>
</dependency>
<!-- HttpClient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,149 @@
package com.snow.common.utils;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.request.*;
/**
* 认证授权工具类
*
* @author ruoyi
*/
public class AuthUtils
{
@SuppressWarnings("deprecation")
public static AuthRequest getAuthRequest(String source, String clientId, String clientSecret, String redirectUri,
AuthStateCache authStateCache)
{
AuthRequest authRequest = null;
switch (source.toLowerCase())
{
case "dingtalk":
authRequest = new AuthDingTalkRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "baidu":
authRequest = new AuthBaiduRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "github":
authRequest = new AuthGithubRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "gitee":
authRequest = new AuthGiteeRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "weibo":
authRequest = new AuthWeiboRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "coding":
authRequest = new AuthCodingRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).codingGroupName("").build(), authStateCache);
break;
case "oschina":
authRequest = new AuthOschinaRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "alipay":
// 支付宝在创建回调地址时不允许使用localhost或者127.0.0.1所以这儿的回调地址使用的局域网内的ip
authRequest = new AuthAlipayRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.alipayPublicKey("").redirectUri(redirectUri).build(), authStateCache);
break;
case "qq":
authRequest = new AuthQqRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "wechat_open":
authRequest = new AuthWeChatOpenRequest(AuthConfig.builder().clientId(clientId)
.clientSecret(clientSecret).redirectUri(redirectUri).build(), authStateCache);
break;
case "csdn":
authRequest = new AuthCsdnRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "taobao":
authRequest = new AuthTaobaoRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "douyin":
authRequest = new AuthDouyinRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "linkedin":
authRequest = new AuthLinkedinRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "microsoft":
authRequest = new AuthMicrosoftRequest(AuthConfig.builder().clientId(clientId)
.clientSecret(clientSecret).redirectUri(redirectUri).build(), authStateCache);
break;
case "mi":
authRequest = new AuthMiRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "toutiao":
authRequest = new AuthToutiaoRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "teambition":
authRequest = new AuthTeambitionRequest(AuthConfig.builder().clientId(clientId)
.clientSecret(clientSecret).redirectUri(redirectUri).build(), authStateCache);
break;
case "pinterest":
authRequest = new AuthPinterestRequest(AuthConfig.builder().clientId(clientId)
.clientSecret(clientSecret).redirectUri(redirectUri).build(), authStateCache);
break;
case "renren":
authRequest = new AuthRenrenRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "stack_overflow":
authRequest = new AuthStackOverflowRequest(AuthConfig.builder().clientId(clientId)
.clientSecret(clientSecret).redirectUri(redirectUri).stackOverflowKey("").build(),
authStateCache);
break;
case "huawei":
authRequest = new AuthHuaweiRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "wechat_enterprise":
authRequest = new AuthWeChatEnterpriseRequest(AuthConfig.builder().clientId(clientId)
.clientSecret(clientSecret).redirectUri(redirectUri).agentId("").build(), authStateCache);
break;
case "kujiale":
authRequest = new AuthKujialeRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "gitlab":
authRequest = new AuthGitlabRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "meituan":
authRequest = new AuthMeituanRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "eleme":
authRequest = new AuthElemeRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build());
break;
case "wechat_mp":
authRequest = new AuthWeChatMpRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
case "aliyun":
authRequest = new AuthAliyunRequest(AuthConfig.builder().clientId(clientId).clientSecret(clientSecret)
.redirectUri(redirectUri).build(), authStateCache);
break;
default:
break;
}
if (null == authRequest)
{
throw new AuthException("未获取到有效的Auth配置");
}
return authRequest;
}
}

View File

@ -7,6 +7,7 @@ import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.Filter;
import com.snow.common.constant.Constants;
import com.snow.framework.shiro.realm.UserRealm;
import com.snow.framework.shiro.session.OnlineSessionDAO;
import com.snow.framework.shiro.session.OnlineSessionFactory;
@ -175,6 +176,7 @@ public class ShiroConfig
public UserRealm userRealm(EhCacheManager cacheManager)
{
UserRealm userRealm = new UserRealm();
userRealm.setAuthorizationCacheName(Constants.SYS_AUTH_CACHE);
userRealm.setCacheManager(cacheManager);
return userRealm;
}

View File

@ -0,0 +1,30 @@
package com.snow.framework.shiro.auth;
/**
* 登录类型枚举类
*
* @author ruoyi
*/
public enum LoginType
{
/**
* 密码登录
*/
PASSWORD("password"),
/**
* 免密码登录
*/
NOPASSWD("nopasswd");
private String desc;
LoginType(String desc)
{
this.desc = desc;
}
public String getDesc()
{
return desc;
}
}

View File

@ -0,0 +1,47 @@
package com.snow.framework.shiro.auth;
import org.apache.shiro.authc.UsernamePasswordToken;
/**
* 自定义登录Token
*
* @author ruoyi
*/
public class UserToken extends UsernamePasswordToken
{
private static final long serialVersionUID = 1L;
private LoginType type;
public UserToken()
{
}
public UserToken(String username, String password, LoginType type, boolean rememberMe)
{
super(username, password, rememberMe);
this.type = type;
}
public UserToken(String username, LoginType type)
{
super(username, "", false, null);
this.type = type;
}
public UserToken(String username, String password, LoginType type)
{
super(username, password, false, null);
this.type = type;
}
public LoginType getType()
{
return type;
}
public void setType(LoginType type)
{
this.type = type;
}
}

View File

@ -2,6 +2,9 @@ package com.snow.framework.shiro.realm;
import java.util.HashSet;
import java.util.Set;
import com.snow.framework.shiro.auth.LoginType;
import com.snow.framework.shiro.auth.UserToken;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
@ -14,6 +17,7 @@ import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
@ -85,9 +89,9 @@ public class UserRealm extends AuthorizingRealm
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
{
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
UserToken upToken = (UserToken) token;
String username = upToken.getUsername();
String loginType = upToken.getHost();
LoginType loginType = upToken.getType();
String password = "";
if (upToken.getPassword() != null)
{
@ -98,7 +102,12 @@ public class UserRealm extends AuthorizingRealm
try
{
user = loginService.login(username, password);
if(LoginType.PASSWORD.equals(loginType)){
user = loginService.login(username, password);
}else if(LoginType.NOPASSWD.equals(loginType)){
user = loginService.login(username);
}
}
catch (CaptchaException e)
{
@ -140,4 +149,19 @@ public class UserRealm extends AuthorizingRealm
{
this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
}
/**
* 清理所有用户授权信息缓存
*/
public void clearAllCachedAuthorizationInfo()
{
Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
if (cache != null)
{
for (Object key : cache.keys())
{
cache.remove(key);
}
}
}
}

View File

@ -105,7 +105,57 @@ public class SysLoginService
recordLoginInfo(user);
return user;
}
/**
* 登录
*/
public SysUser login(String username)
{
// 验证码校验
if (!StringUtils.isEmpty(ServletUtils.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA)))
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
throw new CaptchaException();
}
// 用户名或密码为空 错误
if (StringUtils.isEmpty(username))
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
throw new UserNotExistsException();
}
// 用户名不在指定范围内 错误
if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|| username.length() > UserConstants.USERNAME_MAX_LENGTH)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
}
// 查询用户信息
SysUser user = userService.selectUserByLoginName(username);
if (user == null)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.not.exists")));
throw new UserNotExistsException();
}
if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.delete")));
throw new UserDeleteException();
}
if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.blocked", user.getRemark())));
throw new UserBlockedException();
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
recordLoginInfo(user);
return user;
}
private boolean maybeEmail(String username)
{
if (!username.matches(UserConstants.EMAIL_PATTERN))

View File

@ -0,0 +1,117 @@
package com.snow.system.domain;
import com.snow.common.core.domain.BaseEntity;
/**
* 第三方授权表 sys_auth_user
*
* @author ruoyi
*/
public class SysAuthUser extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 授权ID */
private Long authId;
/** 第三方平台用户唯一ID */
private String uuid;
/** 系统用户ID */
private Long userId;
/** 登录账号 */
private String loginName;
/** 用户昵称 */
private String userName;
/** 头像地址 */
private String avatar;
/** 用户邮箱 */
private String email;
/** 用户来源 */
private String source;
public Long getAuthId()
{
return authId;
}
public void setAuthId(Long authId)
{
this.authId = authId;
}
public String getUuid()
{
return uuid;
}
public void setUuid(String uuid)
{
this.uuid = uuid;
}
public Long getUserId()
{
return userId;
}
public void setUserId(Long userId)
{
this.userId = userId;
}
public String getLoginName()
{
return loginName;
}
public void setLoginName(String loginName)
{
this.loginName = loginName;
}
public String getUserName()
{
return userName;
}
public void setUserName(String userName)
{
this.userName = userName;
}
public String getAvatar()
{
return avatar;
}
public void setAvatar(String avatar)
{
this.avatar = avatar;
}
public String getEmail()
{
return email;
}
public void setEmail(String email)
{
this.email = email;
}
public String getSource()
{
return source;
}
public void setSource(String source)
{
this.source = source;
}
}

View File

@ -3,6 +3,7 @@ package com.snow.system.mapper;
import java.util.List;
import java.util.Set;
import com.snow.system.domain.SysAuthUser;
import com.snow.system.domain.SysUser;
import org.apache.ibatis.annotations.Param;
@ -145,4 +146,46 @@ public interface SysUserMapper
* @return 结果
*/
public SysUser checkEmailUnique(String email);
/**
* 根据uuid查询用户信息
*
* @param uuid 唯一信息
* @return 结果
*/
public SysUser selectAuthUserByUuid(String uuid);
/**
* 新增第三方授权信息
*
* @param authUser 用户信息
* @return 结果
*/
public int insertAuthUser(SysAuthUser authUser);
/**
* 根据用户编号查询授权列表
*
* @param loginName 登录账户
* @return 授权列表
*/
public List<SysAuthUser> selectAuthUserListByUserId(Long userId);
/**
* 校验source平台是否绑定
*
* @param userId 用户编号
* @param source 绑定平台
* @return 结果
*/
public int checkAuthUser(@Param("userId") Long userId, @Param("source") String source);
/**
* 根据编号删除第三方授权信息
*
* @param authId 授权编号
* @return 结果
*/
public int deleteAuthUser(Long authId);
}

View File

@ -3,6 +3,7 @@ package com.snow.system.service;
import java.util.List;
import java.util.Set;
import com.snow.system.domain.SysAuthUser;
import com.snow.system.domain.SysUser;
import com.snow.system.domain.SysUserRole;
@ -220,4 +221,11 @@ public interface ISysUserService
* @return 结果
*/
public int changeStatus(SysUser user);
/**
* 根据用户编号查询授权列表
*
* @param userId 登录账户
* @return 授权列表
*/
public List<SysAuthUser> selectAuthUserListByUserId(Long userId);
}

View File

@ -5,8 +5,7 @@ import java.util.List;
import java.util.Set;
import com.snow.common.enums.DingTalkListenerType;
import com.snow.system.domain.SysUserPost;
import com.snow.system.domain.SysUserRole;
import com.snow.system.domain.*;
import com.snow.system.event.SyncEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -20,9 +19,6 @@ import com.snow.common.core.text.Convert;
import com.snow.common.exception.BusinessException;
import com.snow.common.utils.StringUtils;
import com.snow.common.utils.security.Md5Utils;
import com.snow.system.domain.SysPost;
import com.snow.system.domain.SysRole;
import com.snow.system.domain.SysUser;
import com.snow.system.mapper.SysPostMapper;
import com.snow.system.mapper.SysRoleMapper;
import com.snow.system.mapper.SysUserMapper;
@ -543,4 +539,15 @@ public class SysUserServiceImpl implements ISysUserService
{
return userMapper.updateUser(user);
}
/**
* 根据用户编号查询授权列表
*
* @param userId 登录账户
* @return 授权列表
*/
public List<SysAuthUser> selectAuthUserListByUserId(Long userId)
{
return userMapper.selectAuthUserListByUserId(userId);
}
}

View File

@ -55,6 +55,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="dataScope" column="data_scope" />
<result property="status" column="role_status" />
</resultMap>
<resultMap id="SysAuthUserResult" type="SysAuthUser">
<id property="authId" column="auth_id" />
<result property="uuid" column="uuid" />
<result property="userId" column="user_id" />
<result property="loginName" column="login_name" />
<result property="userName" column="user_name" />
<result property="avatar" column="avatar" />
<result property="email" column="email" />
<result property="source" column="source" />
<result property="createTime" column="create_time" />
</resultMap>
<sql id="selectUserVo">
select u.user_id, u.dept_id, u.login_name, u.user_name, u.user_type, u.email, u.avatar, u.phonenumber, u.sex, u.password, u.salt, u.status, u.del_flag, u.login_ip, u.login_date, u.pwd_update_date, u.create_time, u.remark,
@ -286,5 +298,46 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
sysdate()
)
</insert>
<select id="selectAuthUserListByUserId" parameterType="Long" resultMap="SysAuthUserResult">
select auth_id, uuid, user_id, login_name, user_name, avatar, email, source, create_time
from sys_auth_user where user_id = #{userId}
</select>
<select id="selectAuthUserByUuid" parameterType="String" resultMap="SysUserResult">
select b.user_id as user_id, b.login_name as login_name, b.password as password
from sys_auth_user a left join sys_user b on a.user_id = b.user_id
where a.uuid = #{uuid} and b.del_flag = '0'
</select>
<select id="checkAuthUser" parameterType="SysAuthUser" resultType="int">
select count(1) from sys_auth_user where user_id=#{userId} and source=#{source} limit 1
</select>
<insert id="insertAuthUser" parameterType="SysAuthUser">
insert into sys_auth_user(
<if test="uuid != null and uuid != ''">uuid,</if>
<if test="userId != null and userId != 0">user_id,</if>
<if test="loginName != null and loginName != ''">login_name,</if>
<if test="userName != null and userName != ''">user_name,</if>
<if test="avatar != null and avatar != ''">avatar,</if>
<if test="email != null and email != ''">email,</if>
<if test="source != null and source != ''">source,</if>
create_time
)values(
<if test="uuid != null and uuid != ''">#{uuid},</if>
<if test="userId != null and userId != 0">#{userId},</if>
<if test="loginName != null and loginName != ''">#{loginName},</if>
<if test="userName != null and userName != ''">#{userName},</if>
<if test="avatar != null and avatar != ''">#{avatar},</if>
<if test="email != null and email != ''">#{email},</if>
<if test="source != null and source != ''">#{source},</if>
sysdate()
)
</insert>
<delete id="deleteAuthUser" parameterType="Long">
delete from sys_auth_user where auth_id = #{authId}
</delete>
</mapper>