Commit cbd69429 by fengshuonan

更新第三方登录模块

parent 3b2dace6
...@@ -43,6 +43,31 @@ CREATE TABLE `database_info` ( ...@@ -43,6 +43,31 @@ CREATE TABLE `database_info` (
INSERT INTO `database_info` VALUES (1, '默认数据库', 'com.mysql.jdbc.Driver', 'root', 'root', 'jdbc:mysql://127.0.0.1:3306/guns?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=CTT', '2019-05-11 13:51:19'); INSERT INTO `database_info` VALUES (1, '默认数据库', 'com.mysql.jdbc.Driver', 'root', 'root', 'jdbc:mysql://127.0.0.1:3306/guns?autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=CONVERT_TO_NULL&useSSL=false&serverTimezone=CTT', '2019-05-11 13:51:19');
-- ---------------------------- -- ----------------------------
-- Table structure for oauth_user_info
-- ----------------------------
DROP TABLE IF EXISTS `oauth_user_info`;
CREATE TABLE `oauth_user_info` (
`oauth_id` bigint(20) NOT NULL COMMENT '主键id',
`user_id` bigint(20) NOT NULL COMMENT '用户主键id',
`nick_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '昵称',
`avatar` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '头像',
`blog` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '用户网址',
`company` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '所在公司',
`location` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '位置',
`email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '邮箱',
`remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '用户备注(各平台中的用户个人介绍)',
`gender` int(11) DEFAULT NULL COMMENT '性别,1-男,0-女',
`source` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '用户来源',
`token` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '用户授权的token',
`uuid` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '第三方平台的用户唯一di',
`create_time` datetime(0) DEFAULT NULL COMMENT '创建时间',
`create_user` bigint(20) DEFAULT NULL COMMENT '创建用户',
`update_time` datetime(0) DEFAULT NULL COMMENT '更新时间',
`update_user` bigint(20) DEFAULT NULL COMMENT '更新用户',
PRIMARY KEY (`oauth_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '第三方用户信息表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for sys_dept -- Table structure for sys_dept
-- ---------------------------- -- ----------------------------
DROP TABLE IF EXISTS `sys_dept`; DROP TABLE IF EXISTS `sys_dept`;
......
### Guns第三方登录,接入qq和码云登录
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.stylefeng</groupId>
<artifactId>guns-vip</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>guns-base-third-login</artifactId>
<packaging>jar</packaging>
<dependencies>
<!--基础组件-->
<dependency>
<groupId>cn.stylefeng</groupId>
<artifactId>guns-sys</artifactId>
<version>1.0.0</version>
</dependency>
<!--第三方登录-->
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>1.6.0-beta</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
</build>
</project>
package cn.stylefeng.guns.oauth.config;
import org.springframework.context.annotation.Configuration;
/**
* 第三方登录配置
*
* @author fengshuonan
* @Date 2019/6/9 16:46
*/
@Configuration
public class OAuthLoginConfig {
}
/**
* Copyright 2018-2020 stylefeng & fengshuonan (https://gitee.com/stylefeng)
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.stylefeng.guns.oauth.core.exception;
import cn.stylefeng.roses.kernel.model.exception.AbstractBaseExceptionEnum;
/**
* 第三方登录异常枚举
*
* @author fengshuonan
* @Date 2019/6/9 18:45
*/
public enum OAuthExceptionEnum implements AbstractBaseExceptionEnum {
OPEN_ID_ALREADY_BIND(500, "当前openId已有人绑定!"),
OAUTH_RESPONSE_ERROR(400, "oauth server错误");
OAuthExceptionEnum(int code, String message) {
this.code = code;
this.message = message;
}
private Integer code;
private String message;
@Override
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
package cn.stylefeng.guns.oauth.core.exception;
import cn.stylefeng.roses.kernel.model.exception.AbstractBaseExceptionEnum;
import cn.stylefeng.roses.kernel.model.exception.ServiceException;
/**
* 第三方登录异常
*
* @author fengshuonan
* @Date 2019/6/9 18:43
*/
public class OAuthLoginException extends ServiceException {
public OAuthLoginException(AbstractBaseExceptionEnum exception) {
super(exception);
}
}
package cn.stylefeng.guns.oauth.core.shiro;
/**
* 登录类型
*
* @author fengshuonan
* @Date 2019/6/9 17:24
*/
public enum LoginType {
PASSWORD, OAUTH_TOKEN
}
package cn.stylefeng.guns.oauth.core.shiro;
import lombok.Data;
import org.apache.shiro.authc.UsernamePasswordToken;
/**
* token登陆类型
*
* @author fengshuonan
* @Date 2019/6/9 17:25
*/
@Data
public class OAuthToken extends UsernamePasswordToken {
private LoginType type;
/**
* 免密登录
*/
public OAuthToken(String username) {
super(username, "", false, null);
this.type = LoginType.OAUTH_TOKEN;
}
/**
* 账号密码登录
*/
public OAuthToken(String username, char[] pwd) {
super(username, pwd, false, null);
this.type = LoginType.PASSWORD;
}
}
package cn.stylefeng.guns.oauth.core.shiro.matcher;
import cn.stylefeng.guns.oauth.core.shiro.LoginType;
import cn.stylefeng.guns.oauth.core.shiro.OAuthToken;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
/**
* 针对旧的shiro密码校验的拓展(带oauth token的登录校验)
*
* @author fengshuonan
* @Date 2019/6/9 19:25
*/
public class WithOAuthTokenMatcher extends HashedCredentialsMatcher {
public WithOAuthTokenMatcher() {
}
public WithOAuthTokenMatcher(String hashAlgorithmName) {
super(hashAlgorithmName);
}
@Override
public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) {
if (authcToken instanceof OAuthToken) {
//如果是oauth token登录,直接跳过密码校验
OAuthToken oAuthToken = (OAuthToken) authcToken;
if (oAuthToken.getType().equals(LoginType.OAUTH_TOKEN)) {
return true;
} else {
return false;
}
} else {
//不是免密登录,调用父类的方法
return super.doCredentialsMatch(authcToken, info);
}
}
}
package cn.stylefeng.guns.oauth.modular.controller;
import cn.stylefeng.guns.oauth.modular.service.LoginService;
import cn.stylefeng.roses.core.base.controller.BaseController;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import static cn.stylefeng.guns.oauth.modular.factory.OAuthRequestFactory.getAuthRequest;
/**
* OAuth统一回调地址
*
* @author fengshuonan
* @Date 2019/6/9 16:38
*/
@Controller
@RequestMapping("/oauth")
@Slf4j
public class OAuthController extends BaseController {
@Autowired
private LoginService loginService;
/**
* 第三方登录跳转
* njui7crdxe
*
* @author fengshuonan
* @Date 2019/6/9 16:44
*/
@RequestMapping("/render/{source}")
public void renderAuth(@PathVariable("source") String source, HttpServletResponse response) throws IOException {
AuthRequest authRequest = getAuthRequest(source);
response.sendRedirect(authRequest.authorize());
}
/**
* 第三方登录成功后的回调地址
*
* @author fengshuonan
* @Date 2019/6/9 16:45
*/
@RequestMapping("/callback/{source}")
public String callback(@PathVariable("source") String source, @RequestParam("code") String code, RedirectAttributes model) {
//通过回调的code,请求对应的oauth server获取用户基本信息和token
AuthRequest authRequest = getAuthRequest(source);
AuthResponse authResponse = authRequest.login(code);
AuthUser oauthUser = (AuthUser) authResponse.getData();
log.info("第三方登录回调成功:" + oauthUser);
//进行第三方用户登录过程
loginService.oauthLogin(oauthUser);
return "redirect:/";
}
}
package cn.stylefeng.guns.oauth.modular.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serializable;
/**
* <p>
* 第三方用户信息表
* </p>
*
* @author stylefeng
* @since 2019-06-09
*/
@TableName("oauth_user_info")
public class OauthUserInfo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
@TableId(value = "oauth_id", type = IdType.ID_WORKER)
private Long oauthId;
/**
* 用户主键id
*/
@TableField("user_id")
private Long userId;
/**
* 昵称
*/
@TableField("nick_name")
private String nickName;
/**
* 头像
*/
@TableField("avatar")
private String avatar;
/**
* 用户网址
*/
@TableField("blog")
private String blog;
/**
* 所在公司
*/
@TableField("company")
private String company;
/**
* 位置
*/
@TableField("location")
private String location;
/**
* 邮箱
*/
@TableField("email")
private String email;
/**
* 用户备注(各平台中的用户个人介绍)
*/
@TableField("remark")
private String remark;
/**
* 性别,1-男,0-女
*/
@TableField("gender")
private Integer gender;
/**
* 用户来源
*/
@TableField("source")
private String source;
/**
* 用户授权的token
*/
@TableField("token")
private String token;
/**
* 第三方平台的用户唯一di
*/
@TableField("uuid")
private String uuid;
/**
* 创建时间
*/
@TableField(value = "create_time", fill = FieldFill.INSERT)
private Date createTime;
/**
* 创建用户
*/
@TableField(value = "create_user", fill = FieldFill.INSERT)
private Long createUser;
/**
* 更新时间
*/
@TableField(value = "update_time", fill = FieldFill.UPDATE)
private Date updateTime;
/**
* 更新用户
*/
@TableField(value = "update_user", fill = FieldFill.UPDATE)
private Long updateUser;
public Long getOauthId() {
return oauthId;
}
public void setOauthId(Long oauthId) {
this.oauthId = oauthId;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public String getBlog() {
return blog;
}
public void setBlog(String blog) {
this.blog = blog;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Long getCreateUser() {
return createUser;
}
public void setCreateUser(Long createUser) {
this.createUser = createUser;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public Long getUpdateUser() {
return updateUser;
}
public void setUpdateUser(Long updateUser) {
this.updateUser = updateUser;
}
@Override
public String toString() {
return "OauthUserInfo{" +
"oauthId=" + oauthId +
", userId=" + userId +
", nickName=" + nickName +
", avatar=" + avatar +
", blog=" + blog +
", company=" + company +
", location=" + location +
", email=" + email +
", remark=" + remark +
", gender=" + gender +
", source=" + source +
", token=" + token +
", uuid=" + uuid +
", createTime=" + createTime +
", createUser=" + createUser +
", updateTime=" + updateTime +
", updateUser=" + updateUser +
"}";
}
}
package cn.stylefeng.guns.oauth.modular.factory;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.request.*;
/**
* OAuth2 请求的构建器
*
* @author fengshuonan
* @Date 2019/6/9 16:49
*/
public class OAuthRequestFactory {
/**
* 服务器基础地址
*/
private static final String BASE_URL = "http://localhost";
/**
* 根据具体的授权来源,获取授权请求工具类
*
* @author fengshuonan
* @Date 2019/6/9 16:49
*/
public static AuthRequest getAuthRequest(String source) {
AuthRequest authRequest = null;
switch (source) {
case "dingtalk":
authRequest = new AuthDingTalkRequest(AuthConfig.builder()
.clientId("")
.clientSecret("")
.redirectUri(BASE_URL + "/oauth/callback/dingtalk")
.build());
break;
case "baidu":
authRequest = new AuthBaiduRequest(AuthConfig.builder()
.clientId("")
.clientSecret("")
.redirectUri(BASE_URL + "/oauth/callback/baidu")
.build());
break;
case "github":
authRequest = new AuthGithubRequest(AuthConfig.builder()
.clientId("")
.clientSecret("")
.redirectUri(BASE_URL + "/oauth/callback/github")
.build());
break;
case "gitee":
authRequest = new AuthGiteeRequest(AuthConfig.builder()
.clientId("")
.clientSecret("")
.redirectUri(BASE_URL + "/oauth/callback/gitee")
.build());
break;
case "weibo":
authRequest = new AuthWeiboRequest(AuthConfig.builder()
.clientId("")
.clientSecret("")
.redirectUri(BASE_URL + "/oauth/callback/weibo")
.build());
break;
case "coding":
authRequest = new AuthCodingRequest(AuthConfig.builder()
.clientId("")
.clientSecret("")
.redirectUri(BASE_URL + "/oauth/callback/tencentCloud")
.build());
break;
case "tencentCloud":
authRequest = new AuthTencentCloudRequest(AuthConfig.builder()
.clientId("")
.clientSecret("")
.redirectUri(BASE_URL + "/oauth/callback/tencentCloud")
.build());
break;
case "oschina":
authRequest = new AuthOschinaRequest(AuthConfig.builder()
.clientId("")
.clientSecret("")
.redirectUri(BASE_URL + "/oauth/callback/oschina")
.build());
break;
case "alipay":
// 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1,所以这儿的回调地址使用的局域网内的ip
authRequest = new AuthAlipayRequest(AuthConfig.builder()
.clientId("")
.clientSecret("")
.alipayPublicKey("")
.redirectUri(BASE_URL + "/oauth/callback/alipay")
.build());
break;
case "qq":
authRequest = new AuthQqRequest(AuthConfig.builder()
.clientId("")
.clientSecret("")
.redirectUri(BASE_URL + "/oauth/callback/qq")
.build());
break;
case "wechat":
authRequest = new AuthWeChatRequest(AuthConfig.builder()
.clientId("")
.clientSecret("")
.redirectUri(BASE_URL + "/oauth/callback/wechat")
.build());
break;
case "csdn":
authRequest = new AuthCsdnRequest(AuthConfig.builder()
.clientId("")
.clientSecret("")
.redirectUri(BASE_URL + "/oauth/callback/wechat")
.build());
break;
case "taobao":
authRequest = new AuthTaobaoRequest(AuthConfig.builder()
.clientId("")
.clientSecret("")
.redirectUri(BASE_URL + "/oauth/callback/taobao")
.build());
break;
case "google":
authRequest = new AuthGoogleRequest(AuthConfig.builder()
.clientId("")
.clientSecret("")
.redirectUri(BASE_URL + "/oauth/callback/google")
.build());
break;
case "facebook":
authRequest = new AuthFacebookRequest(AuthConfig.builder()
.clientId("")
.clientSecret("")
.redirectUri(BASE_URL + "/oauth/callback/facebook")
.build());
break;
}
if (null == authRequest) {
throw new AuthException("未获取到有效的Auth配置");
}
return authRequest;
}
}
package cn.stylefeng.guns.oauth.modular.factory;
import cn.stylefeng.guns.oauth.modular.entity.OauthUserInfo;
import cn.stylefeng.guns.sys.core.constant.state.ManagerStatus;
import cn.stylefeng.guns.sys.core.shiro.ShiroKit;
import cn.stylefeng.guns.sys.modular.system.entity.User;
import cn.stylefeng.roses.core.util.ToolUtil;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.model.AuthUserGender;
import java.util.Date;
/**
* oauth绑定记录
*
* @author fengshuonan
* @Date 2019/6/9 19:02
*/
public class OAuthUserInfoFactory {
/**
* 创建oauth绑定
*
* @author fengshuonan
* @Date 2019/6/9 19:03
*/
public static OauthUserInfo createOAuthUserInfo(Long userId, AuthUser oauthUser) {
OauthUserInfo oauthUserInfo = new OauthUserInfo();
ToolUtil.copyProperties(oauthUser, oauthUserInfo);
//设置openId和第三方源
oauthUserInfo.setUuid(oauthUser.getUuid());
oauthUserInfo.setSource(oauthUser.getSource().name());
//设置本系统地用户id
oauthUserInfo.setUserId(userId);
return oauthUserInfo;
}
/**
* 创建第三方应用在本应用的用户
*
* @author fengshuonan
* @Date 2019/6/9 19:11
*/
public static User createOAuthUser(AuthUser authUser) {
User systemUser = new User();
//设置密码,利用token
String salt = ShiroKit.getRandomSalt(5);
String password = ShiroKit.md5(String.valueOf(authUser.getToken()), salt);
systemUser.setPassword(password);
systemUser.setSalt(salt);
//利用openId设置账号
systemUser.setAccount(authUser.getUsername());
systemUser.setName(authUser.getNickname());
systemUser.setBirthday(new Date());
systemUser.setSex(AuthUserGender.MALE.equals(authUser.getGender()) ? "M" : "F");
systemUser.setEmail("未设置");
systemUser.setPhone("未设置");
//固定第三方应用的角色和部门
systemUser.setRoleId("5");
systemUser.setDeptId(25L);
systemUser.setStatus(ManagerStatus.OK.getCode());
systemUser.setCreateTime(new Date());
systemUser.setCreateUser(1L);
systemUser.setUpdateTime(new Date());
systemUser.setUpdateUser(1L);
systemUser.setVersion(0);
return systemUser;
}
}
package cn.stylefeng.guns.oauth.modular.mapper;
import cn.stylefeng.guns.oauth.modular.entity.OauthUserInfo;
import cn.stylefeng.guns.oauth.modular.model.params.OauthUserInfoParam;
import cn.stylefeng.guns.oauth.modular.model.result.OauthUserInfoResult;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* <p>
* 第三方用户信息表 Mapper 接口
* </p>
*
* @author stylefeng
* @since 2019-06-09
*/
public interface OauthUserInfoMapper extends BaseMapper<OauthUserInfo> {
/**
* 获取列表
*
* @author stylefeng
* @Date 2019-06-09
*/
List<OauthUserInfoResult> customList(@Param("paramCondition") OauthUserInfoParam paramCondition);
/**
* 获取map列表
*
* @author stylefeng
* @Date 2019-06-09
*/
List<Map<String, Object>> customMapList(@Param("paramCondition") OauthUserInfoParam paramCondition);
/**
* 获取分页实体列表
*
* @author stylefeng
* @Date 2019-06-09
*/
Page<OauthUserInfoResult> customPageList(@Param("page") Page page, @Param("paramCondition") OauthUserInfoParam paramCondition);
/**
* 获取分页map列表
*
* @author stylefeng
* @Date 2019-06-09
*/
Page<Map<String, Object>> customPageMapList(@Param("page") Page page, @Param("paramCondition") OauthUserInfoParam paramCondition);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.stylefeng.guns.oauth.modular.mapper.OauthUserInfoMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="cn.stylefeng.guns.oauth.modular.entity.OauthUserInfo">
<id column="oauth_id" property="oauthId" />
<result column="user_id" property="userId" />
<result column="nick_name" property="nickName" />
<result column="avatar" property="avatar" />
<result column="blog" property="blog" />
<result column="company" property="company" />
<result column="location" property="location" />
<result column="email" property="email" />
<result column="remark" property="remark" />
<result column="gender" property="gender" />
<result column="source" property="source" />
<result column="token" property="token" />
<result column="uuid" property="uuid" />
<result column="create_time" property="createTime" />
<result column="create_user" property="createUser" />
<result column="update_time" property="updateTime" />
<result column="update_user" property="updateUser" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
oauth_id AS "oauthId", user_id AS "userId", nick_name AS "nickName", avatar AS "avatar", blog AS "blog", company AS "company", location AS "location", email AS "email", remark AS "remark", gender AS "gender", source AS "source", token AS "token", uuid AS "uuid", create_time AS "createTime", create_user AS "createUser", update_time AS "updateTime", update_user AS "updateUser"
</sql>
<select id="customList" resultType="cn.stylefeng.guns.oauth.modular.model.result.OauthUserInfoResult" parameterType="cn.stylefeng.guns.oauth.modular.model.params.OauthUserInfoParam">
select
<include refid="Base_Column_List"/>
from oauth_user_info where 1 = 1
</select>
<select id="customMapList" resultType="map" parameterType="cn.stylefeng.guns.oauth.modular.model.params.OauthUserInfoParam">
select
<include refid="Base_Column_List"/>
from oauth_user_info where 1 = 1
</select>
<select id="customPageList" resultType="cn.stylefeng.guns.oauth.modular.model.result.OauthUserInfoResult" parameterType="cn.stylefeng.guns.oauth.modular.model.params.OauthUserInfoParam">
select
<include refid="Base_Column_List"/>
from oauth_user_info where 1 = 1
</select>
<select id="customPageMapList" resultType="map" parameterType="cn.stylefeng.guns.oauth.modular.model.params.OauthUserInfoParam">
select
<include refid="Base_Column_List"/>
from oauth_user_info where 1 = 1
</select>
</mapper>
package cn.stylefeng.guns.oauth.modular.model.params;
import lombok.Data;
import cn.stylefeng.roses.kernel.model.validator.BaseValidatingParam;
import java.util.Date;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* <p>
* 第三方用户信息表
* </p>
*
* @author stylefeng
* @since 2019-06-09
*/
@Data
public class OauthUserInfoParam implements Serializable, BaseValidatingParam {
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
private Long oauthId;
/**
* 用户主键id
*/
private Long userId;
/**
* 昵称
*/
private String nickName;
/**
* 头像
*/
private String avatar;
/**
* 用户网址
*/
private String blog;
/**
* 所在公司
*/
private String company;
/**
* 位置
*/
private String location;
/**
* 邮箱
*/
private String email;
/**
* 用户备注(各平台中的用户个人介绍)
*/
private String remark;
/**
* 性别,1-男,0-女
*/
private Integer gender;
/**
* 用户来源
*/
private String source;
/**
* 用户授权的token
*/
private String token;
/**
* 第三方平台的用户唯一di
*/
private String uuid;
/**
* 创建时间
*/
private Date createTime;
/**
* 创建用户
*/
private Long createUser;
/**
* 更新时间
*/
private Date updateTime;
/**
* 更新用户
*/
private Long updateUser;
@Override
public String checkParam() {
return null;
}
}
package cn.stylefeng.guns.oauth.modular.model.result;
import lombok.Data;
import java.util.Date;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* <p>
* 第三方用户信息表
* </p>
*
* @author stylefeng
* @since 2019-06-09
*/
@Data
public class OauthUserInfoResult implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
private Long oauthId;
/**
* 用户主键id
*/
private Long userId;
/**
* 昵称
*/
private String nickName;
/**
* 头像
*/
private String avatar;
/**
* 用户网址
*/
private String blog;
/**
* 所在公司
*/
private String company;
/**
* 位置
*/
private String location;
/**
* 邮箱
*/
private String email;
/**
* 用户备注(各平台中的用户个人介绍)
*/
private String remark;
/**
* 性别,1-男,0-女
*/
private Integer gender;
/**
* 用户来源
*/
private String source;
/**
* 用户授权的token
*/
private String token;
/**
* 第三方平台的用户唯一di
*/
private String uuid;
/**
* 创建时间
*/
private Date createTime;
/**
* 创建用户
*/
private Long createUser;
/**
* 更新时间
*/
private Date updateTime;
/**
* 更新用户
*/
private Long updateUser;
}
package cn.stylefeng.guns.oauth.modular.service;
import me.zhyd.oauth.model.AuthUser;
/**
* 第三方登录服务
*
* @author fengshuonan
* @Date 2019/6/9 17:53
*/
public interface LoginService {
/**
* 第三方登录
*
* @author fengshuonan
* @Date 2019/6/9 18:21
*/
String oauthLogin(AuthUser oauthUser);
}
package cn.stylefeng.guns.oauth.modular.service;
import cn.stylefeng.guns.base.pojo.page.LayuiPageInfo;
import cn.stylefeng.guns.oauth.modular.entity.OauthUserInfo;
import cn.stylefeng.guns.oauth.modular.model.params.OauthUserInfoParam;
import cn.stylefeng.guns.oauth.modular.model.result.OauthUserInfoResult;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
* <p>
* 第三方用户信息表 服务类
* </p>
*
* @author stylefeng
* @since 2019-06-09
*/
public interface OauthUserInfoService extends IService<OauthUserInfo> {
/**
* 新增
*
* @author stylefeng
* @Date 2019-06-09
*/
void add(OauthUserInfoParam param);
/**
* 删除
*
* @author stylefeng
* @Date 2019-06-09
*/
void delete(OauthUserInfoParam param);
/**
* 更新
*
* @author stylefeng
* @Date 2019-06-09
*/
void update(OauthUserInfoParam param);
/**
* 查询单条数据,Specification模式
*
* @author stylefeng
* @Date 2019-06-09
*/
OauthUserInfoResult findBySpec(OauthUserInfoParam param);
/**
* 查询列表,Specification模式
*
* @author stylefeng
* @Date 2019-06-09
*/
List<OauthUserInfoResult> findListBySpec(OauthUserInfoParam param);
/**
* 查询分页数据,Specification模式
*
* @author stylefeng
* @Date 2019-06-09
*/
LayuiPageInfo findPageBySpec(OauthUserInfoParam param);
}
package cn.stylefeng.guns.oauth.modular.service.impl;
import cn.stylefeng.guns.base.shiro.ShiroUser;
import cn.stylefeng.guns.oauth.core.exception.OAuthExceptionEnum;
import cn.stylefeng.guns.oauth.core.exception.OAuthLoginException;
import cn.stylefeng.guns.oauth.core.shiro.OAuthToken;
import cn.stylefeng.guns.oauth.modular.entity.OauthUserInfo;
import cn.stylefeng.guns.oauth.modular.factory.OAuthUserInfoFactory;
import cn.stylefeng.guns.oauth.modular.service.LoginService;
import cn.stylefeng.guns.oauth.modular.service.OauthUserInfoService;
import cn.stylefeng.guns.sys.core.shiro.ShiroKit;
import cn.stylefeng.guns.sys.modular.system.entity.User;
import cn.stylefeng.guns.sys.modular.system.service.UserService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import me.zhyd.oauth.model.AuthUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 默认第三方登录逻辑
*
* @author fengshuonan
* @Date 2019/6/9 18:16
*/
@Service
public class DefaultLoginService implements LoginService {
@Autowired
private UserService userService;
@Autowired
private OauthUserInfoService oauthUserInfoService;
@Override
public String oauthLogin(AuthUser oauthUser) {
if (oauthUser == null) {
throw new OAuthLoginException(OAuthExceptionEnum.OAUTH_RESPONSE_ERROR);
}
//当前有登录用户
if (ShiroKit.isUser()) {
//当前登录用户
ShiroUser user = ShiroKit.getUserNotNull();
//绑定用户相关的openId
bindOAuthUser(user.getId(), oauthUser);
return "redirect:/system/user_info";
} else {
//当前无登录用户,则新创建登录用户
createOAuthUser(oauthUser);
//执行shiro的登录逻辑
OAuthToken token = new OAuthToken(oauthUser.getUsername());
ShiroKit.getSubject().login(token);
return "redirect:/";
}
}
/**
* 绑定当前用户的source和openId
*
* @author fengshuonan
* @Date 2019/6/9 18:51
*/
private void bindOAuthUser(Long userId, AuthUser oauthUser) {
//先判断当前系统这个openId有没有人用
QueryWrapper<OauthUserInfo> queryWrapper = new QueryWrapper<OauthUserInfo>()
.eq("source", oauthUser.getSource().name())
.and(i -> i.eq("uuid", oauthUser.getUuid()))
.and(i -> i.ne("user_id", userId));
List<OauthUserInfo> oauthUserInfos = this.oauthUserInfoService.list(queryWrapper);
//已有人绑定,抛出异常
if (oauthUserInfos != null && oauthUserInfos.size() > 0) {
throw new OAuthLoginException(OAuthExceptionEnum.OPEN_ID_ALREADY_BIND);
}
//新建一条绑定记录
OauthUserInfo oAuthUserInfo = OAuthUserInfoFactory.createOAuthUserInfo(userId, oauthUser);
this.oauthUserInfoService.save(oAuthUserInfo);
}
/**
* 通过第三方登录的信息创建本系统用户
*
* @author fengshuonan
* @Date 2019/6/9 19:07
*/
private void createOAuthUser(AuthUser oauthUser) {
// 判断账号是否重复
User theUser = this.userService.getByAccount(oauthUser.getUsername());
if (theUser != null) {
return;
}
//创建用户
User user = OAuthUserInfoFactory.createOAuthUser(oauthUser);
this.userService.save(user);
//创建第三方绑定信息
this.bindOAuthUser(user.getUserId(), oauthUser);
}
}
package cn.stylefeng.guns.oauth.modular.service.impl;
import cn.stylefeng.guns.base.pojo.page.LayuiPageFactory;
import cn.stylefeng.guns.base.pojo.page.LayuiPageInfo;
import cn.stylefeng.guns.oauth.modular.entity.OauthUserInfo;
import cn.stylefeng.guns.oauth.modular.mapper.OauthUserInfoMapper;
import cn.stylefeng.guns.oauth.modular.model.params.OauthUserInfoParam;
import cn.stylefeng.guns.oauth.modular.model.result.OauthUserInfoResult;
import cn.stylefeng.guns.oauth.modular.service.OauthUserInfoService;
import cn.stylefeng.roses.core.util.ToolUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.io.Serializable;
import java.util.List;
/**
* <p>
* 第三方用户信息表 服务实现类
* </p>
*
* @author stylefeng
* @since 2019-06-09
*/
@Service
public class OauthUserInfoServiceImpl extends ServiceImpl<OauthUserInfoMapper, OauthUserInfo> implements OauthUserInfoService {
@Override
public void add(OauthUserInfoParam param){
OauthUserInfo entity = getEntity(param);
this.save(entity);
}
@Override
public void delete(OauthUserInfoParam param){
this.removeById(getKey(param));
}
@Override
public void update(OauthUserInfoParam param){
OauthUserInfo oldEntity = getOldEntity(param);
OauthUserInfo newEntity = getEntity(param);
ToolUtil.copyProperties(newEntity, oldEntity);
this.updateById(newEntity);
}
@Override
public OauthUserInfoResult findBySpec(OauthUserInfoParam param){
return null;
}
@Override
public List<OauthUserInfoResult> findListBySpec(OauthUserInfoParam param){
return null;
}
@Override
public LayuiPageInfo findPageBySpec(OauthUserInfoParam param){
Page pageContext = getPageContext();
IPage page = this.baseMapper.customPageList(pageContext, param);
return LayuiPageFactory.createPageInfo(page);
}
private Serializable getKey(OauthUserInfoParam param){
return param.getOauthId();
}
private Page getPageContext() {
return LayuiPageFactory.defaultPage();
}
private OauthUserInfo getOldEntity(OauthUserInfoParam param) {
return this.getById(getKey(param));
}
private OauthUserInfo getEntity(OauthUserInfoParam param) {
OauthUserInfo entity = new OauthUserInfo();
ToolUtil.copyProperties(param, entity);
return entity;
}
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.stylefeng.guns.oauth.config.OAuthLoginConfig
\ No newline at end of file
...@@ -31,7 +31,11 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; ...@@ -31,7 +31,11 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration @Configuration
@ConditionalOnProperty(prefix = "guns.muti-datasource", name = "open", havingValue = "false", matchIfMissing = true) @ConditionalOnProperty(prefix = "guns.muti-datasource", name = "open", havingValue = "false", matchIfMissing = true)
@EnableTransactionManagement(proxyTargetClass = true) @EnableTransactionManagement(proxyTargetClass = true)
@MapperScan(basePackages = {"cn.stylefeng.guns.sys.modular.*.mapper", "cn.stylefeng.guns.generator.modular.mapper", "cn.stylefeng.guns.modular.*.mapper","cn.stylefeng.guns.sms.modular.mapper"}) @MapperScan(basePackages = {"cn.stylefeng.guns.sys.modular.*.mapper",
"cn.stylefeng.guns.generator.modular.mapper",
"cn.stylefeng.guns.modular.*.mapper",
"cn.stylefeng.guns.sms.modular.mapper",
"cn.stylefeng.guns.oauth.modular.mapper"})
public class SingleDataSourceConfig { public class SingleDataSourceConfig {
} }
......
...@@ -65,6 +65,6 @@ public interface Const { ...@@ -65,6 +65,6 @@ public interface Const {
/** /**
* 不需要权限验证的资源表达式 * 不需要权限验证的资源表达式
*/ */
List<String> NONE_PERMISSION_RES = CollectionUtil.newLinkedList("/assets/**", "/gunsApi/**", "/login", "/global/sessionError", "/kaptcha", "/error", "/global/error"); List<String> NONE_PERMISSION_RES = CollectionUtil.newLinkedList("/assets/**", "/gunsApi/**", "/login", "/global/sessionError", "/kaptcha", "/error", "/global/error", "/oauth/**");
} }
...@@ -59,7 +59,9 @@ public class GlobalExceptionHandler { ...@@ -59,7 +59,9 @@ public class GlobalExceptionHandler {
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody @ResponseBody
public ErrorResponseData bussiness(ServiceException e) { public ErrorResponseData bussiness(ServiceException e) {
LogManager.me().executeLog(LogTaskFactory.exceptionLog(ShiroKit.getUser().getId(), e)); if (ShiroKit.isUser()) {
LogManager.me().executeLog(LogTaskFactory.exceptionLog(ShiroKit.getUserNotNull().getId(), e));
}
getRequest().setAttribute("tip", e.getMessage()); getRequest().setAttribute("tip", e.getMessage());
log.error("业务异常:", e); log.error("业务异常:", e);
return new ErrorResponseData(e.getCode(), e.getMessage()); return new ErrorResponseData(e.getCode(), e.getMessage());
...@@ -130,7 +132,9 @@ public class GlobalExceptionHandler { ...@@ -130,7 +132,9 @@ public class GlobalExceptionHandler {
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody @ResponseBody
public ErrorResponseData notFount(RuntimeException e) { public ErrorResponseData notFount(RuntimeException e) {
LogManager.me().executeLog(LogTaskFactory.exceptionLog(ShiroKit.getUser().getId(), e)); if (ShiroKit.isUser()) {
LogManager.me().executeLog(LogTaskFactory.exceptionLog(ShiroKit.getUserNotNull().getId(), e));
}
getRequest().setAttribute("tip", "服务器未知运行时异常"); getRequest().setAttribute("tip", "服务器未知运行时异常");
log.error("运行时异常:", e); log.error("运行时异常:", e);
return new ErrorResponseData(BizExceptionEnum.SERVER_ERROR.getCode(), BizExceptionEnum.SERVER_ERROR.getMessage()); return new ErrorResponseData(BizExceptionEnum.SERVER_ERROR.getCode(), BizExceptionEnum.SERVER_ERROR.getMessage());
......
...@@ -16,16 +16,14 @@ ...@@ -16,16 +16,14 @@
package cn.stylefeng.guns.sys.core.shiro; package cn.stylefeng.guns.sys.core.shiro;
import cn.stylefeng.guns.base.shiro.ShiroUser; import cn.stylefeng.guns.base.shiro.ShiroUser;
import cn.stylefeng.guns.sys.modular.system.entity.User;
import cn.stylefeng.guns.sys.core.shiro.service.UserAuthService; import cn.stylefeng.guns.sys.core.shiro.service.UserAuthService;
import cn.stylefeng.guns.sys.core.shiro.service.impl.UserAuthServiceServiceImpl; import cn.stylefeng.guns.sys.core.shiro.service.impl.UserAuthServiceServiceImpl;
import cn.stylefeng.guns.sys.modular.system.entity.User;
import cn.stylefeng.roses.core.util.ToolUtil; import cn.stylefeng.roses.core.util.ToolUtil;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.realm.AuthorizingRealm;
...@@ -81,14 +79,4 @@ public class ShiroDbRealm extends AuthorizingRealm { ...@@ -81,14 +79,4 @@ public class ShiroDbRealm extends AuthorizingRealm {
return info; return info;
} }
/**
* 设置认证加密方式
*/
@Override
public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
HashedCredentialsMatcher md5CredentialsMatcher = new HashedCredentialsMatcher();
md5CredentialsMatcher.setHashAlgorithmName(ShiroKit.hashAlgorithmName);
md5CredentialsMatcher.setHashIterations(ShiroKit.hashIterations);
super.setCredentialsMatcher(md5CredentialsMatcher);
}
} }
...@@ -94,9 +94,8 @@ ...@@ -94,9 +94,8 @@
</div> </div>
<div class="layui-form-item login-other"> <div class="layui-form-item login-other">
<label>第三方登录</label> <label>第三方登录</label>
<a href="javascript:;"><i class="layui-icon layui-icon-login-qq"></i></a> <a href="${ctxPath}/oauth/render/qq"><i class="layui-icon layui-icon-login-qq"></i></a>
<a href="javascript:;"><i class="layui-icon layui-icon-login-wechat"></i></a> <a href="${ctxPath}/oauth/render/gitee"><img style="height: 28px;width: 28px" src="/assets/expand/images/git.png"></a>
<a href="javascript:;"><i class="layui-icon layui-icon-login-weibo"></i></a>
</div> </div>
</form> </form>
</div> </div>
......
...@@ -31,12 +31,19 @@ ...@@ -31,12 +31,19 @@
<version>1.0.0</version> <version>1.0.0</version>
</dependency> </dependency>
<!-- 最新代码生成模块 --> <!-- 分布式job -->
<!-- <dependency>--> <!-- <dependency>-->
<!-- <groupId>cn.stylefeng</groupId>--> <!-- <groupId>cn.stylefeng</groupId>-->
<!-- <artifactId>guns-base-timers</artifactId>--> <!-- <artifactId>guns-base-timers</artifactId>-->
<!-- <version>1.0.0</version>--> <!-- <version>1.0.0</version>-->
<!-- </dependency>--> <!-- </dependency>-->
<!-- 第三方登录 -->
<!-- <dependency>-->
<!-- <groupId>cn.stylefeng</groupId>-->
<!-- <artifactId>guns-base-third-login</artifactId>-->
<!-- <version>1.0.0</version>-->
<!-- </dependency>-->
</dependencies> </dependencies>
......
...@@ -13,12 +13,14 @@ ...@@ -13,12 +13,14 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package cn.stylefeng.guns.sys.config.web; package cn.stylefeng.guns.config;
import cn.stylefeng.guns.sys.core.constant.Const; import cn.stylefeng.guns.sys.core.constant.Const;
import cn.stylefeng.guns.sys.core.properties.GunsProperties; import cn.stylefeng.guns.sys.core.properties.GunsProperties;
import cn.stylefeng.guns.sys.core.shiro.GunsUserFilter; import cn.stylefeng.guns.sys.core.shiro.GunsUserFilter;
import cn.stylefeng.guns.sys.core.shiro.ShiroDbRealm; import cn.stylefeng.guns.sys.core.shiro.ShiroDbRealm;
import cn.stylefeng.guns.sys.core.shiro.ShiroKit;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager; import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager; import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64; import org.apache.shiro.codec.Base64;
...@@ -109,7 +111,13 @@ public class ShiroConfig { ...@@ -109,7 +111,13 @@ public class ShiroConfig {
*/ */
@Bean @Bean
public ShiroDbRealm shiroDbRealm() { public ShiroDbRealm shiroDbRealm() {
return new ShiroDbRealm(); HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName(ShiroKit.hashAlgorithmName);
hashedCredentialsMatcher.setHashIterations(ShiroKit.hashIterations);
ShiroDbRealm shiroDbRealm = new ShiroDbRealm();
shiroDbRealm.setCredentialsMatcher(hashedCredentialsMatcher);
return shiroDbRealm;
} }
/** /**
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
<module>guns-base-sms</module> <module>guns-base-sms</module>
<module>guns-base-email</module> <module>guns-base-email</module>
<module>guns-base-timers</module> <module>guns-base-timers</module>
<module>guns-base-third-login</module>
<module>guns-sys</module> <module>guns-sys</module>
<module>guns-vip-gen</module> <module>guns-vip-gen</module>
<module>guns-vip-main</module> <module>guns-vip-main</module>
...@@ -51,6 +52,13 @@ ...@@ -51,6 +52,13 @@
<dependencies> <dependencies>
<!-- 用just auth的jar冲突-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>4.1.21</version>
</dependency>
<!--核心组件--> <!--核心组件-->
<dependency> <dependency>
<groupId>cn.stylefeng.roses</groupId> <groupId>cn.stylefeng.roses</groupId>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment