diff --git a/http-client.env.json b/http-client.env.json
index c4686b274..c18fbb054 100644
--- a/http-client.env.json
+++ b/http-client.env.json
@@ -2,6 +2,10 @@
"local": {
"baseUrl": "http://127.0.0.1:48080/api",
"userServerUrl": "http://127.0.0.1:28080/api",
- "token": "test1"
+ "token": "test1",
+
+ "userApi": "http://127.0.0.1:48080/app-api",
+ "userToken": "test1",
+ "userTenentId": "1"
}
}
diff --git a/pom.xml b/pom.xml
index c76c9a22d..36015806e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -14,7 +14,6 @@
yudao-user-server
yudao-core-service
yudao-module-member
- yudao-server
${artifactId}
diff --git a/yudao-admin-server/pom.xml b/yudao-admin-server/pom.xml
index 40943697f..bd32454b9 100644
--- a/yudao-admin-server/pom.xml
+++ b/yudao-admin-server/pom.xml
@@ -13,7 +13,11 @@
jar
yudao-admin-server
- 管理后台 Server,提供其 API 接口
+
+ 后端 Server 的主项目,通过引入需要 yudao-module-xxx 的依赖,
+ 从而实现提供 RESTful API 给 yudao-ui-admin、yudao-ui-user 等前端项目。
+ 本质上来说,它就是个空壳(容器)!
+
https://github.com/YunaiV/ruoyi-vue-pro
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/security/SecurityConfiguration.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/security/SecurityConfiguration.java
index a5086b92e..2d911a675 100644
--- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/security/SecurityConfiguration.java
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/framework/security/SecurityConfiguration.java
@@ -33,7 +33,7 @@ public class SecurityConfiguration {
registry.antMatchers(buildAdminApi("/system/sms/callback/**")).anonymous();
// 设置 App API 无需认证
- registry.antMatchers(buildAppApi("/**"));
+ registry.antMatchers(buildAppApi("/**")).permitAll();
};
}
diff --git a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java
index 3f795f83d..c9e537350 100644
--- a/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java
+++ b/yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java
@@ -26,6 +26,7 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.security.core.LoginUser;
+import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthUser;
import org.springframework.context.annotation.Lazy;
@@ -154,7 +155,8 @@ public class SysAuthServiceImpl implements SysAuthService {
try {
// 调用 Spring Security 的 AuthenticationManager#authenticate(...) 方法,使用账号密码进行认证
// 在其内部,会调用到 loadUserByUsername 方法,获取 User 信息
- authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
+ authentication = authenticationManager.authenticate(new MultiUsernamePasswordAuthenticationToken(
+ username, password, getUserType()));
// org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
} catch (BadCredentialsException badCredentialsException) {
this.createLoginLog(username, logTypeEnum, SysLoginResultEnum.BAD_CREDENTIALS);
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/UserTypeEnum.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/UserTypeEnum.java
index 7318fb045..edaba1dd9 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/UserTypeEnum.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/UserTypeEnum.java
@@ -1,5 +1,7 @@
package cn.iocoder.yudao.framework.common.enums;
+import cn.hutool.core.lang.Matcher;
+import cn.hutool.core.util.ArrayUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -22,4 +24,8 @@ public enum UserTypeEnum {
*/
private final String name;
+ public static UserTypeEnum valueOf(Integer value) {
+ return ArrayUtil.firstMatch(userType -> userType.getValue().equals(value), UserTypeEnum.values());
+ }
+
}
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java
index a7f23a391..6cda19f82 100644
--- a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java
+++ b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java
@@ -149,6 +149,7 @@ public class OperateLogAspect {
private static void fillUserFields(OperateLogCreateReqDTO operateLogDTO) {
operateLogDTO.setUserId(WebFrameworkUtils.getLoginUserId());
+ operateLogDTO.setUserType(WebFrameworkUtils.getLoginUserType());
}
private static void fillModuleFields(OperateLogCreateReqDTO operateLogDTO,
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/dto/OperateLogCreateReqDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/dto/OperateLogCreateReqDTO.java
index 1c8f49a26..d676001f2 100644
--- a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/dto/OperateLogCreateReqDTO.java
+++ b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/dto/OperateLogCreateReqDTO.java
@@ -21,6 +21,9 @@ public class OperateLogCreateReqDTO {
@ApiModelProperty(value = "用户编号", required = true, example = "1024")
@NotNull(message = "用户编号不能为空")
private Long userId;
+ @ApiModelProperty(value = "用户类型", required = true, example = "1")
+ @NotNull(message = "用户类型不能为空")
+ private Integer userType;
@ApiModelProperty(value = "操作模块", required = true, example = "订单")
@NotEmpty(message = "操作模块不能为空")
diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java
index ae8b12422..d5953823a 100644
--- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java
+++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java
@@ -1,14 +1,13 @@
package cn.iocoder.yudao.framework.security.config;
import cn.iocoder.yudao.framework.security.core.aop.PreAuthenticatedAspect;
+import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider;
import cn.iocoder.yudao.framework.security.core.context.TransmittableThreadLocalSecurityContextHolderStrategy;
import cn.iocoder.yudao.framework.security.core.filter.JWTAuthenticationTokenFilter;
import cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl;
import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl;
import cn.iocoder.yudao.framework.security.core.handler.LogoutSuccessHandlerImpl;
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
-import cn.iocoder.yudao.framework.security.core.service.SecurityAuthService;
-import cn.iocoder.yudao.framework.security.core.service.SecurityAuthServiceImpl;
import cn.iocoder.yudao.framework.web.config.WebProperties;
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
@@ -68,8 +67,8 @@ public class YudaoSecurityAutoConfiguration {
* 退出处理类 Bean
*/
@Bean
- public LogoutSuccessHandler logoutSuccessHandler(SecurityAuthService securityAuthService) {
- return new LogoutSuccessHandlerImpl(securityProperties, securityAuthService);
+ public LogoutSuccessHandler logoutSuccessHandler(MultiUserDetailsAuthenticationProvider authenticationProvider) {
+ return new LogoutSuccessHandlerImpl(securityProperties, authenticationProvider);
}
/**
@@ -87,18 +86,19 @@ public class YudaoSecurityAutoConfiguration {
* Token 认证过滤器 Bean
*/
@Bean
- public JWTAuthenticationTokenFilter authenticationTokenFilter(SecurityAuthService securityAuthService,
+ public JWTAuthenticationTokenFilter authenticationTokenFilter(MultiUserDetailsAuthenticationProvider authenticationProvider,
GlobalExceptionHandler globalExceptionHandler) {
- return new JWTAuthenticationTokenFilter(securityProperties, securityAuthService, globalExceptionHandler);
+ return new JWTAuthenticationTokenFilter(securityProperties, authenticationProvider, globalExceptionHandler);
}
/**
- * 安全认证的 Service Bean
+ * 身份验证的 Provider Bean,通过它实现账号 + 密码的认证
*/
@Bean
- public SecurityAuthService securityAuthService(List securityFrameworkServices,
- WebProperties webProperties) {
- return new SecurityAuthServiceImpl(securityFrameworkServices, webProperties);
+ public MultiUserDetailsAuthenticationProvider authenticationProvider(
+ List securityFrameworkServices,
+ WebProperties webProperties, PasswordEncoder passwordEncoder) {
+ return new MultiUserDetailsAuthenticationProvider(securityFrameworkServices, webProperties, passwordEncoder);
}
/**
diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
index a00b2c2a6..276ce1ed5 100644
--- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
+++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.security.config;
+import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider;
import cn.iocoder.yudao.framework.security.core.filter.JWTAuthenticationTokenFilter;
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
import cn.iocoder.yudao.framework.web.config.WebProperties;
@@ -35,16 +36,8 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
@Resource
private WebProperties webProperties;
- /**
- * 自定义用户【认证】逻辑
- */
@Resource
- private SecurityAuthFrameworkService userDetailsService;
- /**
- * Spring Security 加密器
- */
- @Resource
- private PasswordEncoder passwordEncoder;
+ private MultiUserDetailsAuthenticationProvider authenticationProvider;
/**
* 认证失败处理类 Bean
*/
@@ -91,8 +84,7 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- auth
- .userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
+ auth.authenticationProvider(authenticationProvider);
}
/**
diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/MultiUserDetailsAuthenticationProvider.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/MultiUserDetailsAuthenticationProvider.java
new file mode 100644
index 000000000..dc8533f96
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/MultiUserDetailsAuthenticationProvider.java
@@ -0,0 +1,149 @@
+package cn.iocoder.yudao.framework.security.core.authentication;
+
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
+import cn.iocoder.yudao.framework.security.core.LoginUser;
+import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
+import cn.iocoder.yudao.framework.web.config.WebProperties;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 支持多用户类型的 AuthenticationProvider 实现类
+ *
+ * 为什么不用 {@link org.springframework.security.authentication.ProviderManager} 呢?
+ * 原因是,需要每个用户类型实现对应的 {@link AuthenticationProvider} + authentication,略显麻烦。实际,也是可以实现的。
+ *
+ * 另外,额外支持 verifyTokenAndRefresh 校验令牌、logout 登出、mockLogin 模拟登陆等操作。
+ * 实际上,它就是 {@link SecurityAuthFrameworkService} 定义的三个接口。
+ * 因为需要支持多种类型,所以需要根据请求的 URL,判断出对应的用户类型,从而使用对应的 SecurityAuthFrameworkService 是吸纳
+ *
+ * @see cn.iocoder.yudao.framework.common.enums.UserTypeEnum
+ * @author 芋道源码
+ */
+public class MultiUserDetailsAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
+
+ private final Map services = new HashMap<>();
+
+ private final WebProperties properties;
+
+ private final PasswordEncoder passwordEncoder;
+
+ public MultiUserDetailsAuthenticationProvider(List serviceList,
+ WebProperties properties, PasswordEncoder passwordEncoder) {
+ serviceList.forEach(service -> services.put(service.getUserType(), service));
+ this.properties = properties;
+ this.passwordEncoder = passwordEncoder;
+ }
+
+ // ========== AuthenticationProvider 相关 ==========
+
+ @Override
+ protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
+ throws AuthenticationException {
+ // 执行用户的加载
+ return selectService(authentication).loadUserByUsername(username);
+ }
+
+ private SecurityAuthFrameworkService selectService(UsernamePasswordAuthenticationToken authentication) {
+ // 第一步,获得用户类型
+ UserTypeEnum userType = getUserType(authentication);
+ // 第二步,获得 SecurityAuthFrameworkService
+ SecurityAuthFrameworkService service = services.get(userType);
+ Assert.notNull(service, "用户类型({}) 找不到 SecurityAuthFrameworkService 实现类", userType);
+ return service;
+ }
+
+ private UserTypeEnum getUserType(UsernamePasswordAuthenticationToken authentication) {
+ Assert.isInstanceOf(MultiUsernamePasswordAuthenticationToken.class, authentication);
+ MultiUsernamePasswordAuthenticationToken multiAuthentication = (MultiUsernamePasswordAuthenticationToken) authentication;
+ UserTypeEnum userType = multiAuthentication.getUserType();
+ Assert.notNull(userType, "用户类型不能为空");
+ return userType;
+ }
+
+ @Override // copy 自 DaoAuthenticationProvider 的 additionalAuthenticationChecks 方法
+ protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication)
+ throws AuthenticationException {
+ // 校验 credentials
+ if (authentication.getCredentials() == null) {
+ this.logger.debug("Failed to authenticate since no credentials provided");
+ throw new BadCredentialsException(this.messages.getMessage(
+ "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
+ }
+ // 校验 password
+ String presentedPassword = authentication.getCredentials().toString();
+ if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
+ this.logger.debug("Failed to authenticate since password does not match stored value");
+ throw new BadCredentialsException(this.messages.getMessage(
+ "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
+ }
+ }
+
+ // ========== SecurityAuthFrameworkService 相关 ==========
+
+ /**
+ * 校验 token 的有效性,并获取用户信息
+ * 通过后,刷新 token 的过期时间
+ *
+ * @param request 请求
+ * @param token token
+ * @return 用户信息
+ */
+ public LoginUser verifyTokenAndRefresh(HttpServletRequest request, String token) {
+ return selectService(request).verifyTokenAndRefresh(token);
+ }
+
+ /**
+ * 模拟指定用户编号的 LoginUser
+ *
+ * @param request 请求
+ * @param userId 用户编号
+ * @return 登录用户
+ */
+ public LoginUser mockLogin(HttpServletRequest request, Long userId) {
+ return selectService(request).mockLogin(userId);
+ }
+
+ /**
+ * 基于 token 退出登录
+ *
+ * @param request 请求
+ * @param token token
+ */
+ public void logout(HttpServletRequest request, String token) {
+ selectService(request).logout(token);
+ }
+
+ private SecurityAuthFrameworkService selectService(HttpServletRequest request) {
+ // 第一步,获得用户类型
+ UserTypeEnum userType = getUserType(request);
+ // 第二步,获得 SecurityAuthFrameworkService
+ SecurityAuthFrameworkService service = services.get(userType);
+ Assert.notNull(service, "URI({}) 用户类型({}) 找不到 SecurityAuthFrameworkService 实现类",
+ request.getRequestURI(), userType);
+ return service;
+ }
+
+ private UserTypeEnum getUserType(HttpServletRequest request) {
+ if (request.getRequestURI().startsWith(properties.getAdminApi().getPrefix())) {
+ return UserTypeEnum.ADMIN;
+ }
+ if (request.getRequestURI().startsWith(properties.getAppApi().getPrefix())) {
+ return UserTypeEnum.MEMBER;
+ }
+ throw new IllegalArgumentException(StrUtil.format("URI({}) 找不到匹配的用户类型", request.getRequestURI()));
+ }
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/MultiUsernamePasswordAuthenticationToken.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/MultiUsernamePasswordAuthenticationToken.java
new file mode 100644
index 000000000..f0bc8dfac
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/MultiUsernamePasswordAuthenticationToken.java
@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.framework.security.core.authentication;
+
+import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
+import lombok.Getter;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+
+import java.util.Collection;
+
+/**
+ * 支持多用户的 UsernamePasswordAuthenticationToken 实现类
+ *
+ * @author 芋道源码
+ */
+@Getter
+public class MultiUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken {
+
+ /**
+ * 用户类型
+ */
+ private UserTypeEnum userType;
+
+ public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials) {
+ super(principal, credentials);
+ }
+
+ public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials,
+ Collection extends GrantedAuthority> authorities) {
+ super(principal, credentials, authorities);
+ }
+
+ public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials, UserTypeEnum userType) {
+ super(principal, credentials);
+ this.userType = userType;
+ }
+
+ public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials,
+ Collection extends GrantedAuthority> authorities, UserTypeEnum userType) {
+ super(principal, credentials, authorities);
+ this.userType = userType;
+ }
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/JWTAuthenticationTokenFilter.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/JWTAuthenticationTokenFilter.java
index 61685e902..804c88d35 100644
--- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/JWTAuthenticationTokenFilter.java
+++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/JWTAuthenticationTokenFilter.java
@@ -5,10 +5,10 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
import cn.iocoder.yudao.framework.security.core.LoginUser;
-import cn.iocoder.yudao.framework.security.core.service.SecurityAuthService;
+import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
-import lombok.AllArgsConstructor;
+import lombok.RequiredArgsConstructor;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
@@ -23,12 +23,12 @@ import java.io.IOException;
*
* @author 芋道源码
*/
-@AllArgsConstructor
+@RequiredArgsConstructor
public class JWTAuthenticationTokenFilter extends OncePerRequestFilter {
private final SecurityProperties securityProperties;
- private final SecurityAuthService authService;
+ private final MultiUserDetailsAuthenticationProvider authenticationProvider;
private final GlobalExceptionHandler globalExceptionHandler;
@@ -40,7 +40,7 @@ public class JWTAuthenticationTokenFilter extends OncePerRequestFilter {
if (StrUtil.isNotEmpty(token)) {
try {
// 验证 token 有效性
- LoginUser loginUser = authService.verifyTokenAndRefresh(request, token);
+ LoginUser loginUser = authenticationProvider.verifyTokenAndRefresh(request, token);
// 模拟 Login 功能,方便日常开发调试
if (loginUser == null) {
loginUser = this.mockLoginUser(request, token);
@@ -78,7 +78,7 @@ public class JWTAuthenticationTokenFilter extends OncePerRequestFilter {
return null;
}
Long userId = Long.valueOf(token.substring(securityProperties.getMockSecret().length()));
- return authService.mockLogin(request, userId);
+ return authenticationProvider.mockLogin(request, userId);
}
}
diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/handler/LogoutSuccessHandlerImpl.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/handler/LogoutSuccessHandlerImpl.java
index e61b351c2..1a642304c 100644
--- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/handler/LogoutSuccessHandlerImpl.java
+++ b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/handler/LogoutSuccessHandlerImpl.java
@@ -4,7 +4,7 @@ import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
-import cn.iocoder.yudao.framework.security.core.service.SecurityAuthService;
+import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import lombok.AllArgsConstructor;
import org.springframework.security.core.Authentication;
@@ -24,14 +24,14 @@ public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
private final SecurityProperties securityProperties;
- private final SecurityAuthService authService;
+ private final MultiUserDetailsAuthenticationProvider authenticationProvider;
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
// 执行退出
String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader());
if (StrUtil.isNotBlank(token)) {
- authService.logout(request, token);
+ authenticationProvider.logout(request, token);
}
// 返回成功
ServletUtils.writeJSON(response, CommonResult.success(null));
diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityAuthService.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityAuthService.java
deleted file mode 100644
index 4fee9cd0d..000000000
--- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityAuthService.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package cn.iocoder.yudao.framework.security.core.service;
-
-import cn.iocoder.yudao.framework.security.core.LoginUser;
-
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * 安全认证的 Service 接口,对 security 组件提供统一的 Auth 相关的方法。
- * 主要是会基于 {@link HttpServletRequest} 参数,匹配对应的 {@link SecurityAuthFrameworkService} 实现,然后调用其方法。
- * 因此,在方法的定义上,和 {@link SecurityAuthFrameworkService} 差不多。
- *
- * @author 芋道源码
- */
-public interface SecurityAuthService {
-
- /**
- * 校验 token 的有效性,并获取用户信息
- * 通过后,刷新 token 的过期时间
- *
- * @param request 请求
- * @param token token
- * @return 用户信息
- */
- LoginUser verifyTokenAndRefresh(HttpServletRequest request, String token);
-
- /**
- * 模拟指定用户编号的 LoginUser
- *
- * @param request 请求
- * @param userId 用户编号
- * @return 登录用户
- */
- LoginUser mockLogin(HttpServletRequest request, Long userId);
-
- /**
- * 基于 token 退出登录
- *
- * @param request 请求
- * @param token token
- */
- void logout(HttpServletRequest request, String token);
-
-}
diff --git a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityAuthServiceImpl.java b/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityAuthServiceImpl.java
deleted file mode 100644
index d9ab8eb0e..000000000
--- a/yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityAuthServiceImpl.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package cn.iocoder.yudao.framework.security.core.service;
-
-import cn.hutool.core.lang.Assert;
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
-import cn.iocoder.yudao.framework.security.core.LoginUser;
-import cn.iocoder.yudao.framework.web.config.WebProperties;
-
-import javax.servlet.http.HttpServletRequest;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * 安全认证的 Service 实现类,基于请求地址,计算到对应的 {@link UserTypeEnum} 枚举,从而拿到对应的 {@link SecurityAuthFrameworkService} 实现
- *
- * @author 芋道源码
- */
-public class SecurityAuthServiceImpl implements SecurityAuthService {
-
- private final Map services = new HashMap<>();
- private final WebProperties properties;
-
- public SecurityAuthServiceImpl(List serviceList, WebProperties properties) {
- serviceList.forEach(service -> services.put(service.getUserType(), service));
- this.properties = properties;
- }
-
- @Override
- public LoginUser verifyTokenAndRefresh(HttpServletRequest request, String token) {
- return selectService(request).verifyTokenAndRefresh(token);
- }
-
- @Override
- public LoginUser mockLogin(HttpServletRequest request, Long userId) {
- return selectService(request).mockLogin(userId);
- }
-
- @Override
- public void logout(HttpServletRequest request, String token) {
- selectService(request).logout(token);
- }
-
- private SecurityAuthFrameworkService selectService(HttpServletRequest request) {
- // 第一步,获得用户类型
- UserTypeEnum userType = getUserType(request);
- // 第二步,获得 SecurityAuthFrameworkService
- SecurityAuthFrameworkService service = services.get(userType);
- Assert.notNull(service, "URI({}) 用户类型({}) 找不到 SecurityAuthFrameworkService 实现类",
- request.getRequestURI(), userType);
- return service;
- }
-
- private UserTypeEnum getUserType(HttpServletRequest request) {
- if (request.getRequestURI().startsWith(properties.getAdminApi().getPrefix())) {
- return UserTypeEnum.ADMIN;
- }
- if (request.getRequestURI().startsWith(properties.getAppApi().getPrefix())) {
- return UserTypeEnum.MEMBER;
- }
- throw new IllegalArgumentException(StrUtil.format("URI({}) 找不到匹配的用户类型", request.getRequestURI()));
- }
-
-}
diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java
index acbfb9c2b..273f34072 100644
--- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java
+++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java
@@ -55,6 +55,11 @@ public class WebFrameworkUtils {
return (Integer) request.getAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_TYPE);
}
+ public static Integer getLoginUserType() {
+ HttpServletRequest request = getRequest();
+ return getLoginUserType(request);
+ }
+
public static Long getLoginUserId() {
HttpServletRequest request = getRequest();
return getLoginUserId(request);
diff --git a/yudao-module-member/yudao-module-member-impl/pom.xml b/yudao-module-member/yudao-module-member-impl/pom.xml
index de4650806..ce7ff84a0 100644
--- a/yudao-module-member/yudao-module-member-impl/pom.xml
+++ b/yudao-module-member/yudao-module-member-impl/pom.xml
@@ -30,6 +30,10 @@
yudao-core-service
+
+ cn.iocoder.boot
+ yudao-spring-boot-starter-biz-operatelog
+
cn.iocoder.boot
yudao-spring-boot-starter-biz-sms
diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http
index c9cadedcc..dbe82122a 100644
--- a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http
+++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http
@@ -1,6 +1,7 @@
### 请求 /login 接口 => 成功
-POST {{userServerUrl}}/login
+POST {{userApi}}/login
Content-Type: application/json
+tenant-id: {{userTenentId}}
{
"mobile": "15601691300",
diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java
index 3876e708b..f52e96d1a 100644
--- a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java
+++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.member.controller.app.auth;
import cn.iocoder.yudao.coreservice.modules.system.service.social.SysSocialCoreService;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
import cn.iocoder.yudao.module.member.service.auth.AuthService;
@@ -40,6 +41,7 @@ public class AppAuthController {
@PostMapping("/login")
@ApiOperation("使用手机 + 密码登录")
+ @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
public CommonResult login(@RequestBody @Valid AppAuthLoginReqVO reqVO) {
String token = authService.login(reqVO, getClientIP(), getUserAgent());
// 返回结果
@@ -48,6 +50,7 @@ public class AppAuthController {
@PostMapping("/sms-login")
@ApiOperation("使用手机 + 验证码登录")
+ @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
public CommonResult smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO) {
String token = authService.smsLogin(reqVO, getClientIP(), getUserAgent());
// 返回结果
@@ -56,12 +59,13 @@ public class AppAuthController {
@PostMapping("/send-sms-code")
@ApiOperation(value = "发送手机验证码")
+ @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
public CommonResult sendSmsCode(@RequestBody @Valid AppAuthSendSmsReqVO reqVO) {
smsCodeService.sendSmsCode(reqVO.getMobile(), reqVO.getScene(), getClientIP());
return success(true);
}
- @GetMapping("/send-sms-code-login")
+ @GetMapping("/send-sms-code-login") // TODO 芋艿:post 比较合理
@ApiOperation(value = "向已登录用户发送验证码",notes = "修改手机时验证原手机号使用")
public CommonResult sendSmsCodeLogin() {
smsCodeService.sendSmsCodeLogin(getLoginUserId());
@@ -71,6 +75,7 @@ public class AppAuthController {
@PostMapping("/reset-password")
@ApiOperation(value = "重置密码", notes = "用户忘记密码时使用")
@PreAuthenticated
+ @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
public CommonResult resetPassword(@RequestBody @Valid AppAuthResetPasswordReqVO reqVO) {
authService.resetPassword(reqVO);
return success(true);
@@ -106,6 +111,7 @@ public class AppAuthController {
@PostMapping("/social-login2")
@ApiOperation("社交登录,使用 手机号 + 手机验证码")
+ @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
public CommonResult socialLogin2(@RequestBody @Valid AppAuthSocialLogin2ReqVO reqVO) {
String token = authService.socialLogin2(reqVO, getClientIP(), getUserAgent());
return success(AppAuthLoginRespVO.builder().token(token).build());
@@ -113,6 +119,7 @@ public class AppAuthController {
@PostMapping("/social-bind")
@ApiOperation("社交绑定,使用 code 授权码")
+ @PreAuthenticated
public CommonResult socialBind(@RequestBody @Valid AppAuthSocialBindReqVO reqVO) {
authService.socialBind(getLoginUserId(), reqVO);
return CommonResult.success(true);
@@ -120,6 +127,7 @@ public class AppAuthController {
@DeleteMapping("/social-unbind")
@ApiOperation("取消社交绑定")
+ @PreAuthenticated
public CommonResult socialUnbind(@RequestBody AppAuthSocialUnbindReqVO reqVO) {
socialService.unbindSocialUser(getLoginUserId(), reqVO.getType(), reqVO.getUnionId(), UserTypeEnum.MEMBER);
return CommonResult.success(true);
diff --git a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/auth/AuthServiceImpl.java b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/auth/AuthServiceImpl.java
index a28b6572d..6deabba12 100644
--- a/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/auth/AuthServiceImpl.java
+++ b/yudao-module-member/yudao-module-member-impl/src/main/java/cn/iocoder/yudao/module/member/service/auth/AuthServiceImpl.java
@@ -14,6 +14,7 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.security.core.LoginUser;
+import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken;
import cn.iocoder.yudao.module.member.controller.app.auth.vo.*;
import cn.iocoder.yudao.module.member.convert.auth.AuthConvert;
import cn.iocoder.yudao.module.member.dal.dataobject.user.UserDO;
@@ -176,7 +177,8 @@ public class AuthServiceImpl implements AuthService {
try {
// 调用 Spring Security 的 AuthenticationManager#authenticate(...) 方法,使用账号密码进行认证
// 在其内部,会调用到 loadUserByUsername 方法,获取 User 信息
- authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
+ authentication = authenticationManager.authenticate(new MultiUsernamePasswordAuthenticationToken(
+ username, password, getUserType()));
} catch (BadCredentialsException badCredentialsException) {
this.createLoginLog(username, logTypeEnum, SysLoginResultEnum.BAD_CREDENTIALS);
throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml
deleted file mode 100644
index 004051314..000000000
--- a/yudao-server/pom.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
- yudao
- cn.iocoder.boot
- ${revision}
-
- 4.0.0
-
- yudao-server
- jar
-
- ${artifactId}
-
- 后端 Server 的主项目,通过引入需要 yudao-module-xxx 的依赖,
- 从而实现提供 RESTful API 给 yudao-ui-admin、yudao-ui-user 等前端项目。
- 本质上来说,它就是个空壳(容器)!
-
-
-
diff --git a/更新日志.md b/更新日志.md
index 5ce39fb84..9ac18dd8e 100644
--- a/更新日志.md
+++ b/更新日志.md
@@ -4,29 +4,30 @@
* 钉钉、飞书等通知
* Vue3 支持
-## [v1.4.0] 计划
-
-* 工作流
- * 修改表单为外置表单
- * 修改请假流程
- * 暂时以用户的岗位作为activiti 的用户组
- * 请假需要请假人部门下具有项目经理岗位, 部门经理, 和人事 岗位的用户
- * 新增 芋道源码部门下 用户 normal(岗位 普通用户) projectmgr(岗位 项目经理) depmgr(岗位 部门经理) hradmin (岗位 人事)
- * 请假流程如下
- 1. 请假人 normal (密码 123456) 登录在我的请假表单,点击新增,填写请假表单
- 2. 如果请假天数<=3, 项目经理 进行审批. 项目经理 projectmgr(密码:123456) 登录 待办请假,中进行审批,可以查看历史跟踪,和流程图
- 3. 如果请假天数>3 需部门经理 进行审批,部门经理depmgr(密码:123456) 登录 待办请假,中进行审批,可以查看历史跟踪,和流程图
- 4. 人事登陆(用户名:hradmin 密码:123456) 登录 待办请假, 中进行审批,可以查看历史跟踪,和流程图
- 5. 流程结束
- * 我的请假中,可以查询本人的请假申请, 和进度
-
### 📝 TODO
* 支付
* 用户前台的社交登陆
* 用户前台的修改手机、修改密码、忘记密码
-## [v1.3.0] 进行中
+## [v1.4.0] 计划,预计 2022.02.28 发布
+
+### ⚠️ Warning
+
+### 📈 Statistic
+
+### ⭐ New Features
+
+*【优化】操作日志新增用户类型,实现 APP 端的 API 的操作日志的记录
+
+### 🐞 Bug Fixes
+
+*【修复】用户无权限访问 指定 API 时,未返回 FORBIDDEN 结果码
+
+### 🔨 Dependency Upgrades
+
+
+## [v1.3.0] 2022.01.24
### ⚠️ Warning