From f3a32821ea88fdfa61b62273982ee3a291bc1c08 Mon Sep 17 00:00:00 2001 From: haoxr <1490493387@qq.com> Date: Tue, 3 Dec 2024 17:15:58 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=89=A9=E5=B1=95=20spring=20secur?= =?UTF-8?q?ity=20=E5=AE=9E=E7=8E=B0=E5=BE=AE=E4=BF=A1=E4=B8=80=E9=94=AE?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E8=AE=A4=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../boot/config/PasswordEncoderConfig.java | 24 +++++ .../youlai/boot/config/SecurityConfig.java | 54 ++++++---- .../WeChatAuthenticationProvider.java | 100 ++++++++++++++++++ .../extension/WeChatAuthenticationToken.java | 69 ++++++++++++ .../service/SysUserDetailsService.java | 3 +- .../auth/service/impl/AuthServiceImpl.java | 86 +++++---------- .../youlai/boot/system/mapper/UserMapper.java | 10 ++ .../boot/system/model/dto/UserAuthInfo.java | 32 +++++- .../youlai/boot/system/model/entity/User.java | 4 +- .../boot/system/service/UserService.java | 16 ++- .../service/impl/ConfigServiceImpl.java | 1 - .../system/service/impl/UserServiceImpl.java | 71 ++++++++++--- .../resources/mapper/system/UserMapper.xml | 23 +++- 13 files changed, 391 insertions(+), 102 deletions(-) create mode 100644 src/main/java/com/youlai/boot/config/PasswordEncoderConfig.java create mode 100644 src/main/java/com/youlai/boot/core/security/extension/WeChatAuthenticationProvider.java create mode 100644 src/main/java/com/youlai/boot/core/security/extension/WeChatAuthenticationToken.java diff --git a/src/main/java/com/youlai/boot/config/PasswordEncoderConfig.java b/src/main/java/com/youlai/boot/config/PasswordEncoderConfig.java new file mode 100644 index 00000000..2d231ba5 --- /dev/null +++ b/src/main/java/com/youlai/boot/config/PasswordEncoderConfig.java @@ -0,0 +1,24 @@ +package com.youlai.boot.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +/** + * + * + * @author haoxr + * @since 2024/12/3 + */ +@Configuration +public class PasswordEncoderConfig { + + /** + * 密码编码器 + */ + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/src/main/java/com/youlai/boot/config/SecurityConfig.java b/src/main/java/com/youlai/boot/config/SecurityConfig.java index e7955cd7..39c9abdf 100644 --- a/src/main/java/com/youlai/boot/config/SecurityConfig.java +++ b/src/main/java/com/youlai/boot/config/SecurityConfig.java @@ -1,5 +1,6 @@ package com.youlai.boot.config; +import cn.binarywang.wx.miniapp.api.WxMaService; import cn.hutool.captcha.generator.CodeGenerator; import cn.hutool.core.collection.CollectionUtil; import com.youlai.boot.common.constant.SecurityConstants; @@ -7,15 +8,20 @@ import com.youlai.boot.config.property.SecurityProperties; import com.youlai.boot.core.filter.RateLimiterFilter; import com.youlai.boot.core.security.exception.MyAccessDeniedHandler; import com.youlai.boot.core.security.exception.MyAuthenticationEntryPoint; -import com.youlai.boot.core.security.filter.JwtValidationFilter; +import com.youlai.boot.core.security.extension.WeChatAuthenticationProvider; import com.youlai.boot.core.security.filter.CaptchaValidationFilter; +import com.youlai.boot.core.security.filter.JwtValidationFilter; +import com.youlai.boot.core.security.service.SysUserDetailsService; import com.youlai.boot.shared.auth.service.impl.JwtTokenService; import com.youlai.boot.system.service.ConfigService; +import com.youlai.boot.system.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; @@ -24,7 +30,6 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @@ -48,6 +53,10 @@ public class SecurityConfig { private final SecurityProperties securityProperties; private final ConfigService configService; private final JwtTokenService jwtTokenService; + private final WxMaService wxMaService; + private final UserService userService; + private final SysUserDetailsService userDetailsService; + private final PasswordEncoder passwordEncoder; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { @@ -69,14 +78,12 @@ public class SecurityConfig { .sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .csrf(AbstractHttpConfigurer::disable) .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) - - ; - // 限流过滤器 - http.addFilterBefore(new RateLimiterFilter(redisTemplate, configService), UsernamePasswordAuthenticationFilter.class); - // 验证码校验过滤器 - http.addFilterBefore(new CaptchaValidationFilter(redisTemplate, codeGenerator), UsernamePasswordAuthenticationFilter.class); - // JWT 校验过滤器 - http.addFilterBefore(new JwtValidationFilter(jwtTokenService), UsernamePasswordAuthenticationFilter.class); + // 限流过滤器 + .addFilterBefore(new RateLimiterFilter(redisTemplate, configService), UsernamePasswordAuthenticationFilter.class) + // 验证码校验过滤器 + .addFilterBefore(new CaptchaValidationFilter(redisTemplate, codeGenerator), UsernamePasswordAuthenticationFilter.class) + // JWT 校验过滤器 + .addFilterBefore(new JwtValidationFilter(jwtTokenService), UsernamePasswordAuthenticationFilter.class); return http.build(); } @@ -94,21 +101,32 @@ public class SecurityConfig { } /** - * 密码编码器 + * 默认密码认证的 Provider */ @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); + public DaoAuthenticationProvider daoAuthenticationProvider() { + DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(); + daoAuthenticationProvider.setPasswordEncoder(passwordEncoder); + daoAuthenticationProvider.setUserDetailsService(userDetailsService); + daoAuthenticationProvider.setHideUserNotFoundExceptions(false); + return daoAuthenticationProvider; } /** - * AuthenticationManager 手动注入 - * - * @param authenticationConfiguration 认证配置 + * 微信认证 Provider */ @Bean - public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { - return authenticationConfiguration.getAuthenticationManager(); + public WeChatAuthenticationProvider weChatAuthenticationProvider() { + return new WeChatAuthenticationProvider(userService, wxMaService); } + /** + * 手动注入 AuthenticationManager,支持多种认证方式 + * - DaoAuthenticationProvider:用户名密码认证 + * - WeChatAuthenticationProvider:微信认证 + */ + @Bean + public AuthenticationManager authenticationManager() { + return new ProviderManager(daoAuthenticationProvider(), weChatAuthenticationProvider()); + } } diff --git a/src/main/java/com/youlai/boot/core/security/extension/WeChatAuthenticationProvider.java b/src/main/java/com/youlai/boot/core/security/extension/WeChatAuthenticationProvider.java new file mode 100644 index 00000000..41602133 --- /dev/null +++ b/src/main/java/com/youlai/boot/core/security/extension/WeChatAuthenticationProvider.java @@ -0,0 +1,100 @@ +package com.youlai.boot.core.security.extension; + +import cn.binarywang.wx.miniapp.api.WxMaService; +import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.youlai.boot.core.security.model.SysUserDetails; +import com.youlai.boot.system.model.dto.UserAuthInfo; +import com.youlai.boot.system.service.UserService; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.error.WxErrorException; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.CredentialsExpiredException; +import org.springframework.security.authentication.DisabledException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + + +/** + * 微信认证 Provider + * + * @author Ray.Hao + * @since 2.17.0 + */ +@Slf4j +public class WeChatAuthenticationProvider implements AuthenticationProvider { + + private final UserService userService; + + private final WxMaService wxMaService; + + + public WeChatAuthenticationProvider(UserService userService, WxMaService wxMaService) { + this.userService = userService; + this.wxMaService = wxMaService; + } + + + /** + * 微信认证逻辑,参考 Spring Security 认证密码校验流程 + * + * @param authentication 认证对象 + * @return 认证后的 Authentication 对象 + * @throws AuthenticationException 认证异常 + * @see org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider#authenticate(Authentication) + */ + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + String code = (String) authentication.getPrincipal(); + + // 通过微信服务端验证 code 并获取用户会话信息 + WxMaJscode2SessionResult sessionInfo; + try { + sessionInfo = wxMaService.getUserService().getSessionInfo(code); + } catch (WxErrorException e) { + throw new CredentialsExpiredException("微信登录 code 无效或已失效,请重新获取"); + } + + String openId = sessionInfo.getOpenid(); + if (StrUtil.isBlank(openId)) { + throw new UsernameNotFoundException("未能获取到微信 OpenID,请稍后重试"); + } + + // 根据微信 OpenID 查询用户信息 + UserAuthInfo userAuthInfo = userService.getUserAuthInfoByOpenId(openId); + + if (userAuthInfo == null) { + // TODO: 用户不存在则注册,这里需要获取用户手机号并与现有用户绑定 + userService.registerOrBindWechatUser(openId); + + // 再次查询用户信息,确保用户注册成功 + userAuthInfo = userService.getUserAuthInfoByOpenId(openId); + if (userAuthInfo == null) { + throw new UsernameNotFoundException("用户注册失败,请稍后重试"); + } + } + + // 检查用户状态是否有效 + if (ObjectUtil.notEqual(userAuthInfo.getStatus(), 1)) { + throw new DisabledException("用户已被禁用"); + } + // 这里因为已经根据 code 从微信小程序获取到 openid 不需要再经过系统认证,所以直接生成 + + // 构建认证后的用户详情信息 + SysUserDetails userDetails = new SysUserDetails(userAuthInfo); + + // 创建已认证的 WeChatAuthenticationToken + WeChatAuthenticationToken weChatAuthenticationToken = WeChatAuthenticationToken.authenticated( + userDetails, + userDetails.getAuthorities() + ); + return weChatAuthenticationToken; + } + + @Override + public boolean supports(Class authentication) { + return WeChatAuthenticationToken.class.isAssignableFrom(authentication); + } +} diff --git a/src/main/java/com/youlai/boot/core/security/extension/WeChatAuthenticationToken.java b/src/main/java/com/youlai/boot/core/security/extension/WeChatAuthenticationToken.java new file mode 100644 index 00000000..fc8c7fcb --- /dev/null +++ b/src/main/java/com/youlai/boot/core/security/extension/WeChatAuthenticationToken.java @@ -0,0 +1,69 @@ +package com.youlai.boot.core.security.extension; + +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; + +import java.io.Serial; +import java.util.Collection; + +/** + * 微信认证 Token + * + * @author Ray.Hao + * @since 2024/12/2 + */ +public class WeChatAuthenticationToken extends AbstractAuthenticationToken { + @Serial + private static final long serialVersionUID = 621L; + private final Object principal; + + /** + * 微信认证 Token (未认证) + * + * @param principal 微信用户信息 + */ + public WeChatAuthenticationToken(Object principal) { + // 没有授权信息时,设置为 null + super(null); + this.principal = principal; + // 默认未认证 + this.setAuthenticated(false); + } + + + /** + * 微信认证 Token (已认证) + * + * @param principal 微信用户信息 + * @param authorities 授权信息 + */ + public WeChatAuthenticationToken(Object principal, Collection authorities) { + super(authorities); + this.principal = principal; + // 认证通过 + super.setAuthenticated(true); + } + + + /** + * 认证通过 + * + * @param principal 微信用户信息 + * @param authorities 授权信息 + * @return + */ + public static WeChatAuthenticationToken authenticated(Object principal, Collection authorities) { + return new WeChatAuthenticationToken(principal, authorities); + } + + @Override + public Object getCredentials() { + // 微信认证不需要密码 + return null; + } + + @Override + public Object getPrincipal() { + return this.principal; + } +} diff --git a/src/main/java/com/youlai/boot/core/security/service/SysUserDetailsService.java b/src/main/java/com/youlai/boot/core/security/service/SysUserDetailsService.java index 577d2044..a87c6478 100644 --- a/src/main/java/com/youlai/boot/core/security/service/SysUserDetailsService.java +++ b/src/main/java/com/youlai/boot/core/security/service/SysUserDetailsService.java @@ -13,7 +13,7 @@ import org.springframework.stereotype.Service; /** * 系统用户认证 DetailsService * - * @author Ray + * @author Ray.Hao * @since 2021/10/19 */ @Service @@ -39,7 +39,6 @@ public class SysUserDetailsService implements UserDetailsService { } return new SysUserDetails(userAuthInfo); } catch (Exception e) { - e.printStackTrace(); // 记录异常日志 log.error("认证异常:{}", e.getMessage()); // 抛出异常 diff --git a/src/main/java/com/youlai/boot/shared/auth/service/impl/AuthServiceImpl.java b/src/main/java/com/youlai/boot/shared/auth/service/impl/AuthServiceImpl.java index 54e7243b..ff030b10 100644 --- a/src/main/java/com/youlai/boot/shared/auth/service/impl/AuthServiceImpl.java +++ b/src/main/java/com/youlai/boot/shared/auth/service/impl/AuthServiceImpl.java @@ -11,6 +11,7 @@ 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.result.ResultCode; +import com.youlai.boot.core.security.extension.WeChatAuthenticationToken; import com.youlai.boot.core.security.util.SecurityUtils; import com.youlai.boot.shared.auth.enums.CaptchaTypeEnum; import com.youlai.boot.shared.auth.model.RefreshTokenRequest; @@ -57,24 +58,44 @@ public class AuthServiceImpl implements AuthService { private final UserService userService; /** - * 登录 + * 用户名密码登录 * * @param username 用户名 * @param password 密码 - * @return 登录结果 + * @return 访问令牌 */ @Override public AuthTokenResponse login(String username, String password) { - // 创建认证令牌对象 + // 1. 创建用于密码认证的令牌(未认证) UsernamePasswordAuthenticationToken authenticationToken = - new UsernamePasswordAuthenticationToken(username.toLowerCase().trim(), password); - // 执行用户认证,认证成功返回的Authentication是SysUserDetailsService#loadUserByUsername获取到的的UserDetails + new UsernamePasswordAuthenticationToken(username.trim(), password); + // 2. 执行认证(认证中) Authentication authentication = authenticationManager.authenticate(authenticationToken); - // 认证成功后生成JWT令牌 + + // 3. 认证成功后生成 JWT 令牌,并存入 Security 上下文,供登录日志 AOP 使用(已认证) AuthTokenResponse authTokenResponse = tokenService.generateToken(authentication); - // 将认证信息存入Security上下文,便于在AOP(如日志记录)中获取当前用户信息 SecurityContextHolder.getContext().setAuthentication(authentication); - // 返回包含JWT令牌的登录结果 + return authTokenResponse; + } + + /** + * 微信一键授权登录 + * + * @param code 微信登录code + * @return 访问令牌 + */ + @Override + public AuthTokenResponse wechatLogin(String code) { + // 1. 创建用户微信认证的令牌(未认证) + WeChatAuthenticationToken authenticationToken = new WeChatAuthenticationToken(code); + + // 2. 执行认证(认证中) + Authentication authentication = authenticationManager.authenticate(authenticationToken); + + // 3. 认证成功后生成 JWT 令牌,并存入 Security 上下文,供登录日志 AOP 使用(已认证) + AuthTokenResponse authTokenResponse = tokenService.generateToken(authentication); + SecurityContextHolder.getContext().setAuthentication(authentication); + return authTokenResponse; } @@ -158,54 +179,5 @@ public class AuthServiceImpl implements AuthService { 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; - } } diff --git a/src/main/java/com/youlai/boot/system/mapper/UserMapper.java b/src/main/java/com/youlai/boot/system/mapper/UserMapper.java index a1534127..ca1a3bff 100644 --- a/src/main/java/com/youlai/boot/system/mapper/UserMapper.java +++ b/src/main/java/com/youlai/boot/system/mapper/UserMapper.java @@ -48,6 +48,14 @@ public interface UserMapper extends BaseMapper { */ UserAuthInfo getUserAuthInfo(String username); + /** + * 根据微信openid获取用户认证信息 + * + * @param openid 微信openid + * @return + */ + UserAuthInfo getUserAuthInfoByOpenId(String openid); + /** * 获取导出用户列表 * @@ -64,4 +72,6 @@ public interface UserMapper extends BaseMapper { * @return */ UserBO getUserProfile(Long userId); + + } diff --git a/src/main/java/com/youlai/boot/system/model/dto/UserAuthInfo.java b/src/main/java/com/youlai/boot/system/model/dto/UserAuthInfo.java index 0f4e0880..fff8242d 100644 --- a/src/main/java/com/youlai/boot/system/model/dto/UserAuthInfo.java +++ b/src/main/java/com/youlai/boot/system/model/dto/UserAuthInfo.java @@ -7,29 +7,57 @@ import java.util.Set; /** * 用户认证信息 * - * @author haoxr + * @author Ray.Hao * @since 2022/10/22 - * */ @Data public class UserAuthInfo { + /** + * 用户ID + */ private Long userId; + /** + * 用户名 + */ private String username; + /** + * 昵称 + */ private String nickname; + /** + * 部门ID + */ private Long deptId; + /** + * 用户密码 + */ private String password; + /** + * 状态(1:启用;0:禁用) + */ private Integer status; + /** + * 用户所属的角色集合 + */ private Set roles; + /** + * 用户拥有的权限集合 + */ private Set perms; + /** + * 数据权限范围,用于控制用户可以访问的数据级别 + * + * @see com.youlai.boot.common.enums.DataScopeEnum + */ private Integer dataScope; } diff --git a/src/main/java/com/youlai/boot/system/model/entity/User.java b/src/main/java/com/youlai/boot/system/model/entity/User.java index 71e74f7f..3bea52be 100644 --- a/src/main/java/com/youlai/boot/system/model/entity/User.java +++ b/src/main/java/com/youlai/boot/system/model/entity/User.java @@ -74,7 +74,7 @@ public class User extends BaseEntity { private Integer isDeleted; /** - * 微信openid + * 微信 OpenID */ - private String openId; + private String openid; } \ No newline at end of file diff --git a/src/main/java/com/youlai/boot/system/service/UserService.java b/src/main/java/com/youlai/boot/system/service/UserService.java index 189d4abb..e68db0c9 100644 --- a/src/main/java/com/youlai/boot/system/service/UserService.java +++ b/src/main/java/com/youlai/boot/system/service/UserService.java @@ -160,10 +160,18 @@ public interface UserService extends IService { List> listUserOptions(); /** - * 根据openId获取用户信息 + * 根据 openid 获取用户认证信息 * - * @param openId openId - * @return {@link User} + * @param username 用户名 + * @return {@link UserAuthInfo} */ - User getUserByOpenId(String openId); + + UserAuthInfo getUserAuthInfoByOpenId(String username); + + /** + * 根据微信 OpenID 注册或绑定用户 + * + * @param openId 微信 OpenID + */ + void registerOrBindWechatUser(String openId); } diff --git a/src/main/java/com/youlai/boot/system/service/impl/ConfigServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/ConfigServiceImpl.java index 90904347..b7cea7d7 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/ConfigServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/ConfigServiceImpl.java @@ -35,7 +35,6 @@ import java.util.stream.Collectors; @RequiredArgsConstructor public class ConfigServiceImpl extends ServiceImpl implements ConfigService { - private final ConfigMapper configMapper; private final ConfigConverter configConverter; diff --git a/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java index 880b290f..a94b220b 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java @@ -16,6 +16,7 @@ import com.youlai.boot.common.model.Option; import com.youlai.boot.shared.mail.service.MailService; import com.youlai.boot.shared.sms.service.SmsService; import com.youlai.boot.system.model.entity.User; +import com.youlai.boot.system.model.entity.UserRole; import com.youlai.boot.system.model.form.*; import com.youlai.boot.config.property.AliyunSmsProperties; import com.youlai.boot.system.converter.UserConverter; @@ -61,8 +62,6 @@ public class UserServiceImpl extends ServiceImpl implements Us private final UserRoleService userRoleService; - private final UserConverter userConverter; - private final RoleMenuService roleMenuService; private final RoleService roleService; @@ -79,6 +78,8 @@ public class UserServiceImpl extends ServiceImpl implements Us private final TokenService tokenService; + private final UserConverter userConverter; + /** * 获取用户分页列表 * @@ -214,11 +215,63 @@ public class UserServiceImpl extends ServiceImpl implements Us } + /** + * 根据 openid 获取用户认证信息 + * + * @param openid 微信 + * @return {@link UserAuthInfo} + */ + @Override + public UserAuthInfo getUserAuthInfoByOpenId(String openid) { + UserAuthInfo userAuthInfo = this.baseMapper.getUserAuthInfoByOpenId(openid); + if (userAuthInfo != null) { + Set roles = userAuthInfo.getRoles(); + if (CollectionUtil.isNotEmpty(roles)) { + Set perms = roleMenuService.getRolePermsByRoleCodes(roles); + userAuthInfo.setPerms(perms); + } + + // 获取最大范围的数据权限 + Integer dataScope = roleService.getMaximumDataScope(roles); + userAuthInfo.setDataScope(dataScope); + } + return userAuthInfo; + } + + /** + * 根据微信 OpenID 注册或绑定用户 + *

+ * TODO 根据手机号绑定用户 + * + * @param openId 微信 OpenID + */ + @Override + public void registerOrBindWechatUser(String openId) { + User user = this.getOne( + new LambdaQueryWrapper().eq(User::getOpenid, openId) + ); + if (user == null) { + user = new User(); + user.setNickname("微信用户"); // 默认昵称 + user.setUsername(openId); // TODO 后续替换为手机号 + user.setOpenid(openId); + user.setGender(0); // 保密 + user.setUpdateBy(SecurityUtils.getUserId()); + user.setPassword(SystemConstants.DEFAULT_PASSWORD); + this.save(user); + // 为了默认系统管理员角色,这里按需调整,实际情况绑定已存在的系统用户,另一种情况是给默认游客角色,然后由系统管理员设置用户的角色 + UserRole userRole = new UserRole(); + userRole.setUserId(user.getId()); + userRole.setRoleId(1L); // TODO 系统管理员 + userRoleService.save(userRole); + } + } + /** * 获取导出用户列表 * * @param queryParams 查询参数 - * @return {@link List< UserExportDTO >} 导出用户列表 + * @return {@link List} 导出用户列表 */ @Override public List listExportUsers(UserPageQuery queryParams) { @@ -319,7 +372,7 @@ public class UserServiceImpl extends ServiceImpl implements Us .set(User::getPassword, passwordEncoder.encode(newPassword)) ); - if(result){ + if (result) { // 加入黑名单,重新登录 String accessToken = SecurityUtils.getTokenFromRequest(); tokenService.blacklistToken(accessToken); @@ -458,14 +511,4 @@ public class UserServiceImpl extends ServiceImpl implements Us return Collections.emptyList(); } - /** - * 根据openId获取用户信息 - * - * @param openId openId - * @return {@link User} - */ - @Override - public User getUserByOpenId(String openId) { - return this.getOne(new LambdaQueryWrapper().eq(User::getOpenId, openId)); - } } diff --git a/src/main/resources/mapper/system/UserMapper.xml b/src/main/resources/mapper/system/UserMapper.xml index d508315a..56034645 100644 --- a/src/main/resources/mapper/system/UserMapper.xml +++ b/src/main/resources/mapper/system/UserMapper.xml @@ -123,7 +123,7 @@ - + + + + @@ -196,4 +214,5 @@ u.id = #{userId} AND u.is_deleted = 0 +