Merge branch 'master' of https://gitee.com/youlaiorg/youlai-boot
This commit is contained in:
9
pom.xml
9
pom.xml
@@ -54,6 +54,9 @@
|
|||||||
<!-- 阿里云短信 -->
|
<!-- 阿里云短信 -->
|
||||||
<aliyun.java.sdk.core.version>4.6.4</aliyun.java.sdk.core.version>
|
<aliyun.java.sdk.core.version>4.6.4</aliyun.java.sdk.core.version>
|
||||||
<aliyun.java.sdk.dysmsapi.version>2.2.1</aliyun.java.sdk.dysmsapi.version>
|
<aliyun.java.sdk.dysmsapi.version>2.2.1</aliyun.java.sdk.dysmsapi.version>
|
||||||
|
|
||||||
|
<!-- 微信 jdk -->
|
||||||
|
<weixin-java.version>4.5.5.B</weixin-java.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@@ -223,6 +226,12 @@
|
|||||||
<version>${aliyun.java.sdk.dysmsapi.version}</version>
|
<version>${aliyun.java.sdk.dysmsapi.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.binarywang</groupId>
|
||||||
|
<artifactId>weixin-java-miniapp</artifactId>
|
||||||
|
<version>${weixin-java.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -405,6 +405,7 @@ CREATE TABLE `sys_user` (
|
|||||||
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
|
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
|
||||||
`update_by` bigint NULL DEFAULT NULL COMMENT '修改人ID',
|
`update_by` bigint NULL DEFAULT NULL COMMENT '修改人ID',
|
||||||
`is_deleted` tinyint(1) NULL DEFAULT 0 COMMENT '逻辑删除标识(0-未删除 1-已删除)',
|
`is_deleted` tinyint(1) NULL DEFAULT 0 COMMENT '逻辑删除标识(0-未删除 1-已删除)',
|
||||||
|
`open_id` char(28) DEFAULT NULL COMMENT '微信 openid',
|
||||||
PRIMARY KEY (`id`) USING BTREE,
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
UNIQUE INDEX `login_name`(`username` ASC) USING BTREE
|
UNIQUE INDEX `login_name`(`username` ASC) USING BTREE
|
||||||
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户信息表' ROW_FORMAT = DYNAMIC;
|
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户信息表' ROW_FORMAT = DYNAMIC;
|
||||||
|
|||||||
@@ -367,6 +367,7 @@
|
|||||||
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
|
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
|
||||||
`update_by` bigint NULL DEFAULT NULL COMMENT '修改人ID',
|
`update_by` bigint NULL DEFAULT NULL COMMENT '修改人ID',
|
||||||
`is_deleted` tinyint(1) NULL DEFAULT 0 COMMENT '逻辑删除标识(0-未删除 1-已删除)',
|
`is_deleted` tinyint(1) NULL DEFAULT 0 COMMENT '逻辑删除标识(0-未删除 1-已删除)',
|
||||||
|
`open_id` char(28) DEFAULT NULL COMMENT '微信 openid',
|
||||||
PRIMARY KEY (`id`) USING BTREE,
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
UNIQUE INDEX `login_name`(`username` ASC) USING BTREE
|
UNIQUE INDEX `login_name`(`username` ASC) USING BTREE
|
||||||
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户信息表' ROW_FORMAT = DYNAMIC;
|
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户信息表' ROW_FORMAT = DYNAMIC;
|
||||||
|
|||||||
@@ -35,5 +35,8 @@ public interface SecurityConstants {
|
|||||||
*/
|
*/
|
||||||
String JWT_TOKEN_PREFIX = "Bearer ";
|
String JWT_TOKEN_PREFIX = "Bearer ";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信登录路径
|
||||||
|
*/
|
||||||
|
String WECHAT_LOGIN_PATH = "/api/v1/auth/wechat-login";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,11 @@ public class SecurityConfig {
|
|||||||
http
|
http
|
||||||
|
|
||||||
.authorizeHttpRequests(requestMatcherRegistry ->
|
.authorizeHttpRequests(requestMatcherRegistry ->
|
||||||
requestMatcherRegistry.requestMatchers(SecurityConstants.LOGIN_PATH).permitAll()
|
requestMatcherRegistry
|
||||||
|
.requestMatchers(
|
||||||
|
SecurityConstants.LOGIN_PATH,
|
||||||
|
SecurityConstants.WECHAT_LOGIN_PATH)
|
||||||
|
.permitAll()
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
)
|
)
|
||||||
.exceptionHandling(httpSecurityExceptionHandlingConfigurer ->
|
.exceptionHandling(httpSecurityExceptionHandlingConfigurer ->
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.youlai.boot.config;
|
||||||
|
|
||||||
|
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||||
|
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
|
||||||
|
import cn.binarywang.wx.miniapp.config.WxMaConfig;
|
||||||
|
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置微信 appId 和 appSecret
|
||||||
|
*
|
||||||
|
* @author wangtao
|
||||||
|
* @since 2024/11/26 17:28
|
||||||
|
*/
|
||||||
|
@Setter
|
||||||
|
@ConfigurationProperties(prefix = "wechat.miniapp")
|
||||||
|
@Configuration
|
||||||
|
public class WechatMiniAppConfig {
|
||||||
|
|
||||||
|
private String appId;
|
||||||
|
|
||||||
|
private String appSecret;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public WxMaConfig wxMaConfig() {
|
||||||
|
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
|
||||||
|
config.setAppid(appId);
|
||||||
|
config.setSecret(appSecret);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public WxMaService wxMaService(WxMaConfig wxMaConfig) {
|
||||||
|
WxMaService service = new WxMaServiceImpl();
|
||||||
|
service.setWxMaConfig(wxMaConfig);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,4 +61,14 @@ public class AuthController {
|
|||||||
AuthTokenResponse authTokenResponse = authService.refreshToken(request);
|
AuthTokenResponse authTokenResponse = authService.refreshToken(request);
|
||||||
return Result.success(authTokenResponse);
|
return Result.success(authTokenResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "微信登录")
|
||||||
|
@PostMapping("/wechat-login")
|
||||||
|
@Log(value = "微信登录", module = LogModuleEnum.LOGIN)
|
||||||
|
public Result<AuthTokenResponse> wechatLogin(
|
||||||
|
@Parameter(description = "微信授权码", example = "code") @RequestParam String code
|
||||||
|
) {
|
||||||
|
AuthTokenResponse loginResult = authService.wechatLogin(code);
|
||||||
|
return Result.success(loginResult);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,4 +40,12 @@ public interface AuthService {
|
|||||||
* @return 登录结果
|
* @return 登录结果
|
||||||
*/
|
*/
|
||||||
AuthTokenResponse refreshToken(RefreshTokenRequest request);
|
AuthTokenResponse refreshToken(RefreshTokenRequest request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信小程序登录
|
||||||
|
*
|
||||||
|
* @param code 微信登录code
|
||||||
|
* @return 登录结果
|
||||||
|
*/
|
||||||
|
AuthTokenResponse wechatLogin(String code);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
package com.youlai.boot.shared.auth.service.impl;
|
package com.youlai.boot.shared.auth.service.impl;
|
||||||
|
|
||||||
|
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||||
|
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
|
||||||
import cn.hutool.captcha.AbstractCaptcha;
|
import cn.hutool.captcha.AbstractCaptcha;
|
||||||
import cn.hutool.captcha.CaptchaUtil;
|
import cn.hutool.captcha.CaptchaUtil;
|
||||||
import cn.hutool.captcha.generator.CodeGenerator;
|
import cn.hutool.captcha.generator.CodeGenerator;
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.youlai.boot.common.constant.SecurityConstants;
|
import com.youlai.boot.common.constant.SecurityConstants;
|
||||||
|
import com.youlai.boot.common.constant.SystemConstants;
|
||||||
import com.youlai.boot.common.exception.BusinessException;
|
import com.youlai.boot.common.exception.BusinessException;
|
||||||
import com.youlai.boot.common.result.ResultCode;
|
import com.youlai.boot.common.result.ResultCode;
|
||||||
import com.youlai.boot.core.security.util.SecurityUtils;
|
import com.youlai.boot.core.security.util.SecurityUtils;
|
||||||
@@ -16,8 +19,12 @@ import com.youlai.boot.shared.auth.model.CaptchaResponse;
|
|||||||
import com.youlai.boot.shared.auth.model.AuthTokenResponse;
|
import com.youlai.boot.shared.auth.model.AuthTokenResponse;
|
||||||
import com.youlai.boot.config.property.CaptchaProperties;
|
import com.youlai.boot.config.property.CaptchaProperties;
|
||||||
import com.youlai.boot.shared.auth.service.TokenService;
|
import com.youlai.boot.shared.auth.service.TokenService;
|
||||||
|
import com.youlai.boot.system.model.entity.User;
|
||||||
|
import com.youlai.boot.system.model.form.UserForm;
|
||||||
|
import com.youlai.boot.system.service.UserService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import me.chanjar.weixin.common.error.WxErrorException;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
@@ -26,6 +33,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,6 +53,8 @@ public class AuthServiceImpl implements AuthService {
|
|||||||
private final Font captchaFont;
|
private final Font captchaFont;
|
||||||
private final CaptchaProperties captchaProperties;
|
private final CaptchaProperties captchaProperties;
|
||||||
private final TokenService tokenService;
|
private final TokenService tokenService;
|
||||||
|
private final WxMaService wxMaService;
|
||||||
|
private final UserService userService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录
|
* 登录
|
||||||
@@ -148,4 +158,54 @@ public class AuthServiceImpl implements AuthService {
|
|||||||
return tokenService.refreshToken(refreshToken);
|
return tokenService.refreshToken(refreshToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信小程序登录
|
||||||
|
*
|
||||||
|
* @param code 微信登录code
|
||||||
|
* @return 访问令牌
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AuthTokenResponse wechatLogin(String code) {
|
||||||
|
// 1. 通过code获取微信access_token
|
||||||
|
WxMaJscode2SessionResult sessionInfo = null;
|
||||||
|
try {
|
||||||
|
sessionInfo = wxMaService.getUserService().getSessionInfo(code);
|
||||||
|
} catch (WxErrorException e) {
|
||||||
|
log.error("微信小程序登录失败", e);
|
||||||
|
throw new BusinessException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
String openId = sessionInfo.getOpenid();
|
||||||
|
if (StrUtil.isBlank(openId)) {
|
||||||
|
throw new BusinessException("微信授权失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo 获取微信用户信息
|
||||||
|
// WxMaUserInfo userInfo = wxMaService.getUserService().getUserInfo(sessionInfo.getSessionKey(), sessionInfo.getOpenid());
|
||||||
|
|
||||||
|
// 2. 根据openId查询用户信息,如果不存在则注册新用户
|
||||||
|
User user = userService.getUserByOpenId(openId);
|
||||||
|
if (Objects.isNull(user)) {
|
||||||
|
String name = "微信用户" + IdUtil.simpleUUID();
|
||||||
|
UserForm newUser = new UserForm();
|
||||||
|
newUser.setOpenId(openId);
|
||||||
|
newUser.setNickname(name);
|
||||||
|
newUser.setUsername(name);
|
||||||
|
boolean result = userService.saveUser(newUser);
|
||||||
|
if (!result) {
|
||||||
|
throw new BusinessException("微信用户注册失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UsernamePasswordAuthenticationToken authenticationToken =
|
||||||
|
new UsernamePasswordAuthenticationToken(user.getUsername().toLowerCase().trim(), SystemConstants.DEFAULT_PASSWORD);
|
||||||
|
// 执行用户认证
|
||||||
|
Authentication authentication = authenticationManager.authenticate(authenticationToken);
|
||||||
|
// 认证成功后生成JWT令牌
|
||||||
|
AuthTokenResponse authTokenResponse = tokenService.generateToken(authentication);
|
||||||
|
// 将认证信息存入Security上下文,便于在AOP(如日志记录)中获取当前用户信息
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
|
// 返回包含JWT令牌的登录结果
|
||||||
|
return authTokenResponse;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class ConfigController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "刷新系统配置缓存")
|
@Operation(summary = "刷新系统配置缓存")
|
||||||
@PutMapping(value = "/refresh")
|
@PutMapping("/refresh")
|
||||||
@PreAuthorize("@ss.hasPerm('sys:config:refresh')")
|
@PreAuthorize("@ss.hasPerm('sys:config:refresh')")
|
||||||
public Result<ConfigForm> refreshCache() {
|
public Result<ConfigForm> refreshCache() {
|
||||||
return Result.judge(configService.refreshCache());
|
return Result.judge(configService.refreshCache());
|
||||||
|
|||||||
@@ -83,8 +83,7 @@ public class NoticeController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "发布通知公告")
|
@Operation(summary = "发布通知公告")
|
||||||
@PatchMapping(value = "/{id}/publish")
|
@PutMapping("/{id}/publish")
|
||||||
@PutMapping(value = "/{id}/publish")
|
|
||||||
@PreAuthorize("@ss.hasPerm('sys:notice:publish')")
|
@PreAuthorize("@ss.hasPerm('sys:notice:publish')")
|
||||||
public Result<Void> publishNotice(
|
public Result<Void> publishNotice(
|
||||||
@Parameter(description = "通知公告ID") @PathVariable Long id
|
@Parameter(description = "通知公告ID") @PathVariable Long id
|
||||||
@@ -94,8 +93,7 @@ public class NoticeController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "撤回通知公告")
|
@Operation(summary = "撤回通知公告")
|
||||||
@PutMapping(value = "/{id}/revoke")
|
@PutMapping("/{id}/revoke")
|
||||||
@PatchMapping(value = "/{id}/revoke")
|
|
||||||
@PreAuthorize("@ss.hasPerm('sys:notice:revoke')")
|
@PreAuthorize("@ss.hasPerm('sys:notice:revoke')")
|
||||||
public Result<Void> revokeNotice(
|
public Result<Void> revokeNotice(
|
||||||
@Parameter(description = "通知公告ID") @PathVariable Long id
|
@Parameter(description = "通知公告ID") @PathVariable Long id
|
||||||
|
|||||||
@@ -72,4 +72,9 @@ public class User extends BaseEntity {
|
|||||||
* 是否删除(0-否 1-是)
|
* 是否删除(0-否 1-是)
|
||||||
*/
|
*/
|
||||||
private Integer isDeleted;
|
private Integer isDeleted;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信openid
|
||||||
|
*/
|
||||||
|
private String openId;
|
||||||
}
|
}
|
||||||
@@ -56,4 +56,7 @@ public class UserForm {
|
|||||||
@NotEmpty(message = "用户角色不能为空")
|
@NotEmpty(message = "用户角色不能为空")
|
||||||
private List<Long> roleIds;
|
private List<Long> roleIds;
|
||||||
|
|
||||||
|
@Schema(description="微信openId")
|
||||||
|
private String openId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,4 +158,12 @@ public interface UserService extends IService<User> {
|
|||||||
* @return {@link List<Option<String>>} 用户选项列表
|
* @return {@link List<Option<String>>} 用户选项列表
|
||||||
*/
|
*/
|
||||||
List<Option<String>> listUserOptions();
|
List<Option<String>> listUserOptions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据openId获取用户信息
|
||||||
|
*
|
||||||
|
* @param openId openId
|
||||||
|
* @return {@link User}
|
||||||
|
*/
|
||||||
|
User getUserByOpenId(String openId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ public class ConfigServiceImpl extends ServiceImpl<ConfigMapper, Config> impleme
|
|||||||
"配置键已存在");
|
"配置键已存在");
|
||||||
Config config = configConverter.toEntity(configForm);
|
Config config = configConverter.toEntity(configForm);
|
||||||
config.setCreateBy(SecurityUtils.getUserId());
|
config.setCreateBy(SecurityUtils.getUserId());
|
||||||
|
config.setIsDeleted(0);
|
||||||
return this.save(config);
|
return this.save(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -457,4 +457,15 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
|||||||
}
|
}
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据openId获取用户信息
|
||||||
|
*
|
||||||
|
* @param openId openId
|
||||||
|
* @return {@link User}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public User getUserByOpenId(String openId) {
|
||||||
|
return this.getOne(new LambdaQueryWrapper<User>().eq(User::getOpenId, openId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -214,3 +214,8 @@ captcha:
|
|||||||
# 验证码有效期(秒)
|
# 验证码有效期(秒)
|
||||||
expire-seconds: 120
|
expire-seconds: 120
|
||||||
|
|
||||||
|
# 微信小程序
|
||||||
|
wechat:
|
||||||
|
miniapp:
|
||||||
|
appId: xxxxxx
|
||||||
|
appSecret: xxxxxx
|
||||||
Reference in New Issue
Block a user