Commit 190150d1f4539082a6517a3083e9f724ac659298
Committed by
Gitee
!166 用户 Token 采用 OAuth2 的 Access Token + Refresh Token,提升安全性
Merge pull request !166 from 芋道源码/feature/1.6.2
Showing
117 changed files
with
2771 additions
and
2276 deletions
Too many changes.
To preserve performance only 100 of 117 files are displayed.
yudao-dependencies/pom.xml
| @@ -22,7 +22,6 @@ | @@ -22,7 +22,6 @@ | ||
| 22 | <swagger-annotations.version>1.5.22</swagger-annotations.version> | 22 | <swagger-annotations.version>1.5.22</swagger-annotations.version> |
| 23 | <servlet.versoin>2.5</servlet.versoin> | 23 | <servlet.versoin>2.5</servlet.versoin> |
| 24 | <!-- DB 相关 --> | 24 | <!-- DB 相关 --> |
| 25 | - <mysql.version>5.1.46</mysql.version> | ||
| 26 | <druid.version>1.2.8</druid.version> | 25 | <druid.version>1.2.8</druid.version> |
| 27 | <mybatis-plus.version>3.4.3.4</mybatis-plus.version> | 26 | <mybatis-plus.version>3.4.3.4</mybatis-plus.version> |
| 28 | <mybatis-plus-generator.version>3.5.2</mybatis-plus-generator.version> | 27 | <mybatis-plus-generator.version>3.5.2</mybatis-plus-generator.version> |
| @@ -76,12 +75,6 @@ | @@ -76,12 +75,6 @@ | ||
| 76 | <version>${spring.boot.version}</version> | 75 | <version>${spring.boot.version}</version> |
| 77 | <type>pom</type> | 76 | <type>pom</type> |
| 78 | <scope>import</scope> | 77 | <scope>import</scope> |
| 79 | - <exclusions> | ||
| 80 | - <exclusion> | ||
| 81 | - <groupId>mysql</groupId> | ||
| 82 | - <artifactId>mysql-connector-java</artifactId> | ||
| 83 | - </exclusion> | ||
| 84 | - </exclusions> | ||
| 85 | </dependency> | 78 | </dependency> |
| 86 | 79 | ||
| 87 | <!-- 业务组件 --> | 80 | <!-- 业务组件 --> |
| @@ -180,11 +173,6 @@ | @@ -180,11 +173,6 @@ | ||
| 180 | </dependency> | 173 | </dependency> |
| 181 | 174 | ||
| 182 | <dependency> | 175 | <dependency> |
| 183 | - <groupId>mysql</groupId> | ||
| 184 | - <artifactId>mysql-connector-java</artifactId> | ||
| 185 | - <version>${mysql.version}</version> | ||
| 186 | - </dependency> | ||
| 187 | - <dependency> | ||
| 188 | <groupId>com.alibaba</groupId> | 176 | <groupId>com.alibaba</groupId> |
| 189 | <artifactId>druid-spring-boot-starter</artifactId> | 177 | <artifactId>druid-spring-boot-starter</artifactId> |
| 190 | <version>${druid.version}</version> | 178 | <version>${druid.version}</version> |
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
| @@ -54,6 +54,13 @@ public class CollectionUtils { | @@ -54,6 +54,13 @@ public class CollectionUtils { | ||
| 54 | return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList()); | 54 | return from.stream().map(func).filter(Objects::nonNull).collect(Collectors.toList()); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | + public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func, Predicate<T> filter) { | ||
| 58 | + if (CollUtil.isEmpty(from)) { | ||
| 59 | + return new ArrayList<>(); | ||
| 60 | + } | ||
| 61 | + return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList()); | ||
| 62 | + } | ||
| 63 | + | ||
| 57 | public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) { | 64 | public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) { |
| 58 | if (CollUtil.isEmpty(from)) { | 65 | if (CollUtil.isEmpty(from)) { |
| 59 | return new HashSet<>(); | 66 | return new HashSet<>(); |
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/dept/rule/DeptDataPermissionRule.java
| 1 | package cn.iocoder.yudao.framework.datapermission.core.dept.rule; | 1 | package cn.iocoder.yudao.framework.datapermission.core.dept.rule; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.util.ObjectUtil; | ||
| 4 | import cn.hutool.core.util.StrUtil; | 5 | import cn.hutool.core.util.StrUtil; |
| 5 | -import cn.iocoder.yudao.framework.datapermission.core.dept.service.DeptDataPermissionFrameworkService; | ||
| 6 | -import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO; | 6 | +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 7 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | 7 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 8 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 8 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 9 | +import cn.iocoder.yudao.framework.datapermission.core.dept.service.DeptDataPermissionFrameworkService; | ||
| 10 | +import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO; | ||
| 9 | import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule; | 11 | import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule; |
| 10 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 12 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 11 | import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; | 13 | import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; |
| @@ -13,7 +15,6 @@ import cn.iocoder.yudao.framework.security.core.LoginUser; | @@ -13,7 +15,6 @@ import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 13 | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; | 15 | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; |
| 14 | import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; | 16 | import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; |
| 15 | import lombok.AllArgsConstructor; | 17 | import lombok.AllArgsConstructor; |
| 16 | -import lombok.Getter; | ||
| 17 | import lombok.extern.slf4j.Slf4j; | 18 | import lombok.extern.slf4j.Slf4j; |
| 18 | import net.sf.jsqlparser.expression.Alias; | 19 | import net.sf.jsqlparser.expression.Alias; |
| 19 | import net.sf.jsqlparser.expression.Expression; | 20 | import net.sf.jsqlparser.expression.Expression; |
| @@ -24,10 +25,7 @@ import net.sf.jsqlparser.expression.operators.relational.EqualsTo; | @@ -24,10 +25,7 @@ import net.sf.jsqlparser.expression.operators.relational.EqualsTo; | ||
| 24 | import net.sf.jsqlparser.expression.operators.relational.ExpressionList; | 25 | import net.sf.jsqlparser.expression.operators.relational.ExpressionList; |
| 25 | import net.sf.jsqlparser.expression.operators.relational.InExpression; | 26 | import net.sf.jsqlparser.expression.operators.relational.InExpression; |
| 26 | 27 | ||
| 27 | -import java.util.HashMap; | ||
| 28 | -import java.util.HashSet; | ||
| 29 | -import java.util.Map; | ||
| 30 | -import java.util.Set; | 28 | +import java.util.*; |
| 31 | 29 | ||
| 32 | /** | 30 | /** |
| 33 | * 基于部门的 {@link DataPermissionRule} 数据权限规则实现 | 31 | * 基于部门的 {@link DataPermissionRule} 数据权限规则实现 |
| @@ -50,6 +48,11 @@ import java.util.Set; | @@ -50,6 +48,11 @@ import java.util.Set; | ||
| 50 | @Slf4j | 48 | @Slf4j |
| 51 | public class DeptDataPermissionRule implements DataPermissionRule { | 49 | public class DeptDataPermissionRule implements DataPermissionRule { |
| 52 | 50 | ||
| 51 | + /** | ||
| 52 | + * LoginUser 的 Context 缓存 Key | ||
| 53 | + */ | ||
| 54 | + protected static final String CONTEXT_KEY = DeptDataPermissionRule.class.getSimpleName(); | ||
| 55 | + | ||
| 53 | private static final String DEPT_COLUMN_NAME = "dept_id"; | 56 | private static final String DEPT_COLUMN_NAME = "dept_id"; |
| 54 | private static final String USER_COLUMN_NAME = "user_id"; | 57 | private static final String USER_COLUMN_NAME = "user_id"; |
| 55 | 58 | ||
| @@ -90,13 +93,23 @@ public class DeptDataPermissionRule implements DataPermissionRule { | @@ -90,13 +93,23 @@ public class DeptDataPermissionRule implements DataPermissionRule { | ||
| 90 | if (loginUser == null) { | 93 | if (loginUser == null) { |
| 91 | return null; | 94 | return null; |
| 92 | } | 95 | } |
| 96 | + // 只有管理员类型的用户,才进行数据权限的处理 | ||
| 97 | + if (ObjectUtil.notEqual(loginUser.getUserType(), UserTypeEnum.ADMIN.getValue())) { | ||
| 98 | + return null; | ||
| 99 | + } | ||
| 93 | 100 | ||
| 94 | // 获得数据权限 | 101 | // 获得数据权限 |
| 95 | - DeptDataPermissionRespDTO deptDataPermission = deptDataPermissionService.getDeptDataPermission(loginUser); | 102 | + DeptDataPermissionRespDTO deptDataPermission = loginUser.getContext(CONTEXT_KEY, DeptDataPermissionRespDTO.class); |
| 103 | + // 从上下文中拿不到,则调用逻辑进行获取 | ||
| 96 | if (deptDataPermission == null) { | 104 | if (deptDataPermission == null) { |
| 97 | - log.error("[getExpression][LoginUser({}) 获取数据权限为 null]", JsonUtils.toJsonString(loginUser)); | ||
| 98 | - throw new NullPointerException(String.format("LoginUser(%d) Table(%s/%s) 未返回数据权限", | ||
| 99 | - loginUser.getId(), tableName, tableAlias.getName())); | 105 | + deptDataPermission = deptDataPermissionService.getDeptDataPermission(loginUser.getId()); |
| 106 | + if (deptDataPermission == null) { | ||
| 107 | + log.error("[getExpression][LoginUser({}) 获取数据权限为 null]", JsonUtils.toJsonString(loginUser)); | ||
| 108 | + throw new NullPointerException(String.format("LoginUser(%d) Table(%s/%s) 未返回数据权限", | ||
| 109 | + loginUser.getId(), tableName, tableAlias.getName())); | ||
| 110 | + } | ||
| 111 | + // 添加到上下文中,避免重复计算 | ||
| 112 | + loginUser.setContext(CONTEXT_KEY, deptDataPermission); | ||
| 100 | } | 113 | } |
| 101 | 114 | ||
| 102 | // 情况一,如果是 ALL 可查看全部,则无需拼接条件 | 115 | // 情况一,如果是 ALL 可查看全部,则无需拼接条件 |
| @@ -111,8 +124,8 @@ public class DeptDataPermissionRule implements DataPermissionRule { | @@ -111,8 +124,8 @@ public class DeptDataPermissionRule implements DataPermissionRule { | ||
| 111 | } | 124 | } |
| 112 | 125 | ||
| 113 | // 情况三,拼接 Dept 和 User 的条件,最后组合 | 126 | // 情况三,拼接 Dept 和 User 的条件,最后组合 |
| 114 | - Expression deptExpression = this.buildDeptExpression(tableName,tableAlias, deptDataPermission.getDeptIds()); | ||
| 115 | - Expression userExpression = this.buildUserExpression(tableName, tableAlias, deptDataPermission.getSelf(), loginUser.getId()); | 127 | + Expression deptExpression = buildDeptExpression(tableName,tableAlias, deptDataPermission.getDeptIds()); |
| 128 | + Expression userExpression = buildUserExpression(tableName, tableAlias, deptDataPermission.getSelf(), loginUser.getId()); | ||
| 116 | if (deptExpression == null && userExpression == null) { | 129 | if (deptExpression == null && userExpression == null) { |
| 117 | // TODO 芋艿:获得不到条件的时候,暂时不抛出异常,而是不返回数据 | 130 | // TODO 芋艿:获得不到条件的时候,暂时不抛出异常,而是不返回数据 |
| 118 | log.warn("[getExpression][LoginUser({}) Table({}/{}) DeptDataPermission({}) 构建的条件为空]", | 131 | log.warn("[getExpression][LoginUser({}) Table({}/{}) DeptDataPermission({}) 构建的条件为空]", |
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/dept/service/DeptDataPermissionFrameworkService.java
| 1 | package cn.iocoder.yudao.framework.datapermission.core.dept.service; | 1 | package cn.iocoder.yudao.framework.datapermission.core.dept.service; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO; | 3 | import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO; |
| 4 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 5 | 4 | ||
| 6 | /** | 5 | /** |
| 7 | * 基于部门的数据权限 Framework Service 接口 | 6 | * 基于部门的数据权限 Framework Service 接口 |
| @@ -14,9 +13,9 @@ public interface DeptDataPermissionFrameworkService { | @@ -14,9 +13,9 @@ public interface DeptDataPermissionFrameworkService { | ||
| 14 | /** | 13 | /** |
| 15 | * 获得登陆用户的部门数据权限 | 14 | * 获得登陆用户的部门数据权限 |
| 16 | * | 15 | * |
| 17 | - * @param loginUser 登陆用户 | 16 | + * @param userId 用户编号 |
| 18 | * @return 部门数据权限 | 17 | * @return 部门数据权限 |
| 19 | */ | 18 | */ |
| 20 | - DeptDataPermissionRespDTO getDeptDataPermission(LoginUser loginUser); | 19 | + DeptDataPermissionRespDTO getDeptDataPermission(Long userId); |
| 21 | 20 | ||
| 22 | } | 21 | } |
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/dept/rule/DeptDataPermissionRuleTest.java
| @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.datapermission.core.dept.rule; | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.datapermission.core.dept.rule; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | import cn.hutool.core.util.ReflectUtil; | 4 | import cn.hutool.core.util.ReflectUtil; |
| 5 | +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||
| 5 | import cn.iocoder.yudao.framework.common.util.collection.SetUtils; | 6 | import cn.iocoder.yudao.framework.common.util.collection.SetUtils; |
| 6 | import cn.iocoder.yudao.framework.datapermission.core.dept.service.DeptDataPermissionFrameworkService; | 7 | import cn.iocoder.yudao.framework.datapermission.core.dept.service.DeptDataPermissionFrameworkService; |
| 7 | import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO; | 8 | import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO; |
| @@ -69,7 +70,8 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | @@ -69,7 +70,8 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | ||
| 69 | String tableName = "t_user"; | 70 | String tableName = "t_user"; |
| 70 | Alias tableAlias = new Alias("u"); | 71 | Alias tableAlias = new Alias("u"); |
| 71 | // mock 方法 | 72 | // mock 方法 |
| 72 | - LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)); | 73 | + LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L) |
| 74 | + .setUserType(UserTypeEnum.ADMIN.getValue())); | ||
| 73 | securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); | 75 | securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); |
| 74 | 76 | ||
| 75 | // 调用 | 77 | // 调用 |
| @@ -88,16 +90,18 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | @@ -88,16 +90,18 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | ||
| 88 | String tableName = "t_user"; | 90 | String tableName = "t_user"; |
| 89 | Alias tableAlias = new Alias("u"); | 91 | Alias tableAlias = new Alias("u"); |
| 90 | // mock 方法(LoginUser) | 92 | // mock 方法(LoginUser) |
| 91 | - LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)); | 93 | + LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L) |
| 94 | + .setUserType(UserTypeEnum.ADMIN.getValue())); | ||
| 92 | securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); | 95 | securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); |
| 93 | // mock 方法(DeptDataPermissionRespDTO) | 96 | // mock 方法(DeptDataPermissionRespDTO) |
| 94 | DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO().setAll(true); | 97 | DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO().setAll(true); |
| 95 | - when(deptDataPermissionFrameworkService.getDeptDataPermission(same(loginUser))).thenReturn(deptDataPermission); | 98 | + when(deptDataPermissionFrameworkService.getDeptDataPermission(same(1L))).thenReturn(deptDataPermission); |
| 96 | 99 | ||
| 97 | // 调用 | 100 | // 调用 |
| 98 | Expression expression = rule.getExpression(tableName, tableAlias); | 101 | Expression expression = rule.getExpression(tableName, tableAlias); |
| 99 | // 断言 | 102 | // 断言 |
| 100 | assertNull(expression); | 103 | assertNull(expression); |
| 104 | + assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class)); | ||
| 101 | } | 105 | } |
| 102 | } | 106 | } |
| 103 | 107 | ||
| @@ -109,16 +113,18 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | @@ -109,16 +113,18 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | ||
| 109 | String tableName = "t_user"; | 113 | String tableName = "t_user"; |
| 110 | Alias tableAlias = new Alias("u"); | 114 | Alias tableAlias = new Alias("u"); |
| 111 | // mock 方法(LoginUser) | 115 | // mock 方法(LoginUser) |
| 112 | - LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)); | 116 | + LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L) |
| 117 | + .setUserType(UserTypeEnum.ADMIN.getValue())); | ||
| 113 | securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); | 118 | securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); |
| 114 | // mock 方法(DeptDataPermissionRespDTO) | 119 | // mock 方法(DeptDataPermissionRespDTO) |
| 115 | DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO(); | 120 | DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO(); |
| 116 | - when(deptDataPermissionFrameworkService.getDeptDataPermission(same(loginUser))).thenReturn(deptDataPermission); | 121 | + when(deptDataPermissionFrameworkService.getDeptDataPermission(same(1L))).thenReturn(deptDataPermission); |
| 117 | 122 | ||
| 118 | // 调用 | 123 | // 调用 |
| 119 | Expression expression = rule.getExpression(tableName, tableAlias); | 124 | Expression expression = rule.getExpression(tableName, tableAlias); |
| 120 | // 断言 | 125 | // 断言 |
| 121 | assertEquals("null = null", expression.toString()); | 126 | assertEquals("null = null", expression.toString()); |
| 127 | + assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class)); | ||
| 122 | } | 128 | } |
| 123 | } | 129 | } |
| 124 | 130 | ||
| @@ -130,17 +136,19 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | @@ -130,17 +136,19 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | ||
| 130 | String tableName = "t_user"; | 136 | String tableName = "t_user"; |
| 131 | Alias tableAlias = new Alias("u"); | 137 | Alias tableAlias = new Alias("u"); |
| 132 | // mock 方法(LoginUser) | 138 | // mock 方法(LoginUser) |
| 133 | - LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)); | 139 | + LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L) |
| 140 | + .setUserType(UserTypeEnum.ADMIN.getValue())); | ||
| 134 | securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); | 141 | securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); |
| 135 | // mock 方法(DeptDataPermissionRespDTO) | 142 | // mock 方法(DeptDataPermissionRespDTO) |
| 136 | DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO() | 143 | DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO() |
| 137 | .setDeptIds(SetUtils.asSet(10L, 20L)).setSelf(true); | 144 | .setDeptIds(SetUtils.asSet(10L, 20L)).setSelf(true); |
| 138 | - when(deptDataPermissionFrameworkService.getDeptDataPermission(same(loginUser))).thenReturn(deptDataPermission); | 145 | + when(deptDataPermissionFrameworkService.getDeptDataPermission(same(1L))).thenReturn(deptDataPermission); |
| 139 | 146 | ||
| 140 | // 调用 | 147 | // 调用 |
| 141 | Expression expression = rule.getExpression(tableName, tableAlias); | 148 | Expression expression = rule.getExpression(tableName, tableAlias); |
| 142 | // 断言 | 149 | // 断言 |
| 143 | assertSame(EXPRESSION_NULL, expression); | 150 | assertSame(EXPRESSION_NULL, expression); |
| 151 | + assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class)); | ||
| 144 | } | 152 | } |
| 145 | } | 153 | } |
| 146 | 154 | ||
| @@ -152,12 +160,13 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | @@ -152,12 +160,13 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | ||
| 152 | String tableName = "t_user"; | 160 | String tableName = "t_user"; |
| 153 | Alias tableAlias = new Alias("u"); | 161 | Alias tableAlias = new Alias("u"); |
| 154 | // mock 方法(LoginUser) | 162 | // mock 方法(LoginUser) |
| 155 | - LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)); | 163 | + LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L) |
| 164 | + .setUserType(UserTypeEnum.ADMIN.getValue())); | ||
| 156 | securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); | 165 | securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); |
| 157 | // mock 方法(DeptDataPermissionRespDTO) | 166 | // mock 方法(DeptDataPermissionRespDTO) |
| 158 | DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO() | 167 | DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO() |
| 159 | .setSelf(true); | 168 | .setSelf(true); |
| 160 | - when(deptDataPermissionFrameworkService.getDeptDataPermission(same(loginUser))).thenReturn(deptDataPermission); | 169 | + when(deptDataPermissionFrameworkService.getDeptDataPermission(same(1L))).thenReturn(deptDataPermission); |
| 161 | // 添加 user 字段配置 | 170 | // 添加 user 字段配置 |
| 162 | rule.addUserColumn("t_user", "id"); | 171 | rule.addUserColumn("t_user", "id"); |
| 163 | 172 | ||
| @@ -165,6 +174,7 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | @@ -165,6 +174,7 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | ||
| 165 | Expression expression = rule.getExpression(tableName, tableAlias); | 174 | Expression expression = rule.getExpression(tableName, tableAlias); |
| 166 | // 断言 | 175 | // 断言 |
| 167 | assertEquals("u.id = 1", expression.toString()); | 176 | assertEquals("u.id = 1", expression.toString()); |
| 177 | + assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class)); | ||
| 168 | } | 178 | } |
| 169 | } | 179 | } |
| 170 | 180 | ||
| @@ -176,12 +186,13 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | @@ -176,12 +186,13 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | ||
| 176 | String tableName = "t_user"; | 186 | String tableName = "t_user"; |
| 177 | Alias tableAlias = new Alias("u"); | 187 | Alias tableAlias = new Alias("u"); |
| 178 | // mock 方法(LoginUser) | 188 | // mock 方法(LoginUser) |
| 179 | - LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)); | 189 | + LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L) |
| 190 | + .setUserType(UserTypeEnum.ADMIN.getValue())); | ||
| 180 | securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); | 191 | securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); |
| 181 | // mock 方法(DeptDataPermissionRespDTO) | 192 | // mock 方法(DeptDataPermissionRespDTO) |
| 182 | DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO() | 193 | DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO() |
| 183 | .setDeptIds(CollUtil.newLinkedHashSet(10L, 20L)); | 194 | .setDeptIds(CollUtil.newLinkedHashSet(10L, 20L)); |
| 184 | - when(deptDataPermissionFrameworkService.getDeptDataPermission(same(loginUser))).thenReturn(deptDataPermission); | 195 | + when(deptDataPermissionFrameworkService.getDeptDataPermission(same(1L))).thenReturn(deptDataPermission); |
| 185 | // 添加 dept 字段配置 | 196 | // 添加 dept 字段配置 |
| 186 | rule.addDeptColumn("t_user", "dept_id"); | 197 | rule.addDeptColumn("t_user", "dept_id"); |
| 187 | 198 | ||
| @@ -189,6 +200,7 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | @@ -189,6 +200,7 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | ||
| 189 | Expression expression = rule.getExpression(tableName, tableAlias); | 200 | Expression expression = rule.getExpression(tableName, tableAlias); |
| 190 | // 断言 | 201 | // 断言 |
| 191 | assertEquals("u.dept_id IN (10, 20)", expression.toString()); | 202 | assertEquals("u.dept_id IN (10, 20)", expression.toString()); |
| 203 | + assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class)); | ||
| 192 | } | 204 | } |
| 193 | } | 205 | } |
| 194 | 206 | ||
| @@ -200,12 +212,13 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | @@ -200,12 +212,13 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | ||
| 200 | String tableName = "t_user"; | 212 | String tableName = "t_user"; |
| 201 | Alias tableAlias = new Alias("u"); | 213 | Alias tableAlias = new Alias("u"); |
| 202 | // mock 方法(LoginUser) | 214 | // mock 方法(LoginUser) |
| 203 | - LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)); | 215 | + LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L) |
| 216 | + .setUserType(UserTypeEnum.ADMIN.getValue())); | ||
| 204 | securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); | 217 | securityFrameworkUtilsMock.when(SecurityFrameworkUtils::getLoginUser).thenReturn(loginUser); |
| 205 | // mock 方法(DeptDataPermissionRespDTO) | 218 | // mock 方法(DeptDataPermissionRespDTO) |
| 206 | DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO() | 219 | DeptDataPermissionRespDTO deptDataPermission = new DeptDataPermissionRespDTO() |
| 207 | .setDeptIds(CollUtil.newLinkedHashSet(10L, 20L)).setSelf(true); | 220 | .setDeptIds(CollUtil.newLinkedHashSet(10L, 20L)).setSelf(true); |
| 208 | - when(deptDataPermissionFrameworkService.getDeptDataPermission(same(loginUser))).thenReturn(deptDataPermission); | 221 | + when(deptDataPermissionFrameworkService.getDeptDataPermission(same(1L))).thenReturn(deptDataPermission); |
| 209 | // 添加 user 字段配置 | 222 | // 添加 user 字段配置 |
| 210 | rule.addUserColumn("t_user", "id"); | 223 | rule.addUserColumn("t_user", "id"); |
| 211 | // 添加 dept 字段配置 | 224 | // 添加 dept 字段配置 |
| @@ -215,6 +228,7 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | @@ -215,6 +228,7 @@ class DeptDataPermissionRuleTest extends BaseMockitoUnitTest { | ||
| 215 | Expression expression = rule.getExpression(tableName, tableAlias); | 228 | Expression expression = rule.getExpression(tableName, tableAlias); |
| 216 | // 断言 | 229 | // 断言 |
| 217 | assertEquals("u.dept_id IN (10, 20) OR u.id = 1", expression.toString()); | 230 | assertEquals("u.dept_id IN (10, 20) OR u.id = 1", expression.toString()); |
| 231 | + assertSame(deptDataPermission, loginUser.getContext(DeptDataPermissionRule.CONTEXT_KEY, DeptDataPermissionRespDTO.class)); | ||
| 218 | } | 232 | } |
| 219 | } | 233 | } |
| 220 | 234 |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/web/TenantContextWebFilter.java
| 1 | package cn.iocoder.yudao.framework.tenant.core.web; | 1 | package cn.iocoder.yudao.framework.tenant.core.web; |
| 2 | 2 | ||
| 3 | -import cn.hutool.core.util.StrUtil; | ||
| 4 | import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | 3 | import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; |
| 4 | +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; | ||
| 5 | import org.springframework.web.filter.OncePerRequestFilter; | 5 | import org.springframework.web.filter.OncePerRequestFilter; |
| 6 | 6 | ||
| 7 | import javax.servlet.FilterChain; | 7 | import javax.servlet.FilterChain; |
| @@ -24,9 +24,9 @@ public class TenantContextWebFilter extends OncePerRequestFilter { | @@ -24,9 +24,9 @@ public class TenantContextWebFilter extends OncePerRequestFilter { | ||
| 24 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) | 24 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) |
| 25 | throws ServletException, IOException { | 25 | throws ServletException, IOException { |
| 26 | // 设置 | 26 | // 设置 |
| 27 | - String tenantId = request.getHeader(HEADER_TENANT_ID); | ||
| 28 | - if (StrUtil.isNotEmpty(tenantId)) { | ||
| 29 | - TenantContextHolder.setTenantId(Long.valueOf(tenantId)); | 27 | + Long tenantId = WebFrameworkUtils.getTenantId(request); |
| 28 | + if (tenantId != null) { | ||
| 29 | + TenantContextHolder.setTenantId(tenantId); | ||
| 30 | } | 30 | } |
| 31 | try { | 31 | try { |
| 32 | chain.doFilter(request, response); | 32 | chain.doFilter(request, response); |
yudao-framework/yudao-spring-boot-starter-file/src/main/java/cn/iocoder/yudao/framework/file/core/client/ftp/FtpFileClient.java
| @@ -27,9 +27,11 @@ public class FtpFileClient extends AbstractFileClient<FtpFileClientConfig> { | @@ -27,9 +27,11 @@ public class FtpFileClient extends AbstractFileClient<FtpFileClientConfig> { | ||
| 27 | 27 | ||
| 28 | @Override | 28 | @Override |
| 29 | protected void doInit() { | 29 | protected void doInit() { |
| 30 | - // 补全风格。例如说 Linux 是 /,Windows 是 \ | ||
| 31 | - if (!config.getBasePath().endsWith(File.separator)) { | ||
| 32 | - config.setBasePath(config.getBasePath() + File.separator); | 30 | + // 把配置的 \ 替换成 /, 如果路径配置 \a\test, 替换成 /a/test, 替换方法已经处理 null 情况 |
| 31 | + config.setBasePath(StrUtil.replace(config.getBasePath(), StrUtil.BACKSLASH, StrUtil.SLASH)); | ||
| 32 | + // ftp的路径是 / 结尾 | ||
| 33 | + if (!config.getBasePath().endsWith(StrUtil.SLASH)) { | ||
| 34 | + config.setBasePath(config.getBasePath() + StrUtil.SLASH); | ||
| 33 | } | 35 | } |
| 34 | // 初始化 Ftp 对象 | 36 | // 初始化 Ftp 对象 |
| 35 | this.ftp = new Ftp(config.getHost(), config.getPort(), config.getUsername(), config.getPassword(), | 37 | this.ftp = new Ftp(config.getHost(), config.getPort(), config.getUsername(), config.getPassword(), |
| @@ -42,6 +44,7 @@ public class FtpFileClient extends AbstractFileClient<FtpFileClientConfig> { | @@ -42,6 +44,7 @@ public class FtpFileClient extends AbstractFileClient<FtpFileClientConfig> { | ||
| 42 | String filePath = getFilePath(path); | 44 | String filePath = getFilePath(path); |
| 43 | String fileName = FileUtil.getName(filePath); | 45 | String fileName = FileUtil.getName(filePath); |
| 44 | String dir = StrUtil.removeSuffix(filePath, fileName); | 46 | String dir = StrUtil.removeSuffix(filePath, fileName); |
| 47 | + ftp.reconnectIfTimeout(); | ||
| 45 | boolean success = ftp.upload(dir, fileName, new ByteArrayInputStream(content)); | 48 | boolean success = ftp.upload(dir, fileName, new ByteArrayInputStream(content)); |
| 46 | if (!success) { | 49 | if (!success) { |
| 47 | throw new FtpException(StrUtil.format("上传文件到目标目录 ({}) 失败", filePath)); | 50 | throw new FtpException(StrUtil.format("上传文件到目标目录 ({}) 失败", filePath)); |
| @@ -53,6 +56,7 @@ public class FtpFileClient extends AbstractFileClient<FtpFileClientConfig> { | @@ -53,6 +56,7 @@ public class FtpFileClient extends AbstractFileClient<FtpFileClientConfig> { | ||
| 53 | @Override | 56 | @Override |
| 54 | public void delete(String path) { | 57 | public void delete(String path) { |
| 55 | String filePath = getFilePath(path); | 58 | String filePath = getFilePath(path); |
| 59 | + ftp.reconnectIfTimeout(); | ||
| 56 | ftp.delFile(filePath); | 60 | ftp.delFile(filePath); |
| 57 | } | 61 | } |
| 58 | 62 | ||
| @@ -60,8 +64,9 @@ public class FtpFileClient extends AbstractFileClient<FtpFileClientConfig> { | @@ -60,8 +64,9 @@ public class FtpFileClient extends AbstractFileClient<FtpFileClientConfig> { | ||
| 60 | public byte[] getContent(String path) { | 64 | public byte[] getContent(String path) { |
| 61 | String filePath = getFilePath(path); | 65 | String filePath = getFilePath(path); |
| 62 | String fileName = FileUtil.getName(filePath); | 66 | String fileName = FileUtil.getName(filePath); |
| 63 | - String dir = StrUtil.removeSuffix(path, fileName); | 67 | + String dir = StrUtil.removeSuffix(filePath, fileName); |
| 64 | ByteArrayOutputStream out = new ByteArrayOutputStream(); | 68 | ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| 69 | + ftp.reconnectIfTimeout(); | ||
| 65 | ftp.download(dir, fileName, out); | 70 | ftp.download(dir, fileName, out); |
| 66 | return out.toByteArray(); | 71 | return out.toByteArray(); |
| 67 | } | 72 | } |
| @@ -70,4 +75,4 @@ public class FtpFileClient extends AbstractFileClient<FtpFileClientConfig> { | @@ -70,4 +75,4 @@ public class FtpFileClient extends AbstractFileClient<FtpFileClientConfig> { | ||
| 70 | return config.getBasePath() + path; | 75 | return config.getBasePath() + path; |
| 71 | } | 76 | } |
| 72 | 77 | ||
| 73 | -} | 78 | -} |
| 79 | +} | ||
| 74 | \ No newline at end of file | 80 | \ No newline at end of file |
yudao-framework/yudao-spring-boot-starter-security/pom.xml
| @@ -44,18 +44,11 @@ | @@ -44,18 +44,11 @@ | ||
| 44 | <artifactId>spring-boot-starter-security</artifactId> | 44 | <artifactId>spring-boot-starter-security</artifactId> |
| 45 | </dependency> | 45 | </dependency> |
| 46 | 46 | ||
| 47 | - <!-- TODO 芋艿: --> | 47 | + <!-- 业务组件 --> |
| 48 | <dependency> | 48 | <dependency> |
| 49 | - <groupId>org.activiti</groupId> | ||
| 50 | - <artifactId>activiti-engine</artifactId> | ||
| 51 | - <version>7.1.0.M6</version> | ||
| 52 | - <exclusions> | ||
| 53 | - <exclusion> | ||
| 54 | - <groupId>*</groupId> | ||
| 55 | - <artifactId>*</artifactId> | ||
| 56 | - </exclusion> | ||
| 57 | - </exclusions> | ||
| 58 | - <optional>true</optional> | 49 | + <groupId>cn.iocoder.boot</groupId> |
| 50 | + <artifactId>yudao-module-system-api</artifactId> <!-- 需要使用它,进行 Token 的校验 --> | ||
| 51 | + <version>${revision}</version> | ||
| 59 | </dependency> | 52 | </dependency> |
| 60 | </dependencies> | 53 | </dependencies> |
| 61 | 54 |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/SecurityProperties.java
| @@ -6,7 +6,6 @@ import org.springframework.validation.annotation.Validated; | @@ -6,7 +6,6 @@ import org.springframework.validation.annotation.Validated; | ||
| 6 | 6 | ||
| 7 | import javax.validation.constraints.NotEmpty; | 7 | import javax.validation.constraints.NotEmpty; |
| 8 | import javax.validation.constraints.NotNull; | 8 | import javax.validation.constraints.NotNull; |
| 9 | -import java.time.Duration; | ||
| 10 | 9 | ||
| 11 | @ConfigurationProperties(prefix = "yudao.security") | 10 | @ConfigurationProperties(prefix = "yudao.security") |
| 12 | @Validated | 11 | @Validated |
| @@ -18,18 +17,6 @@ public class SecurityProperties { | @@ -18,18 +17,6 @@ public class SecurityProperties { | ||
| 18 | */ | 17 | */ |
| 19 | @NotEmpty(message = "Token Header 不能为空") | 18 | @NotEmpty(message = "Token Header 不能为空") |
| 20 | private String tokenHeader; | 19 | private String tokenHeader; |
| 21 | - /** | ||
| 22 | - * Token 过期时间 | ||
| 23 | - */ | ||
| 24 | - @NotNull(message = "Token 过期时间不能为空") | ||
| 25 | - private Duration tokenTimeout; | ||
| 26 | - /** | ||
| 27 | - * Session 过期时间 | ||
| 28 | - * | ||
| 29 | - * 当 User 用户超过当前时间未操作,则 Session 会过期 | ||
| 30 | - */ | ||
| 31 | - @NotNull(message = "Session 过期时间不能为空") | ||
| 32 | - private Duration sessionTimeout; | ||
| 33 | 20 | ||
| 34 | /** | 21 | /** |
| 35 | * mock 模式的开关 | 22 | * mock 模式的开关 |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoSecurityAutoConfiguration.java
| 1 | package cn.iocoder.yudao.framework.security.config; | 1 | package cn.iocoder.yudao.framework.security.config; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.security.core.aop.PreAuthenticatedAspect; | 3 | import cn.iocoder.yudao.framework.security.core.aop.PreAuthenticatedAspect; |
| 4 | -import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider; | ||
| 5 | import cn.iocoder.yudao.framework.security.core.context.TransmittableThreadLocalSecurityContextHolderStrategy; | 4 | import cn.iocoder.yudao.framework.security.core.context.TransmittableThreadLocalSecurityContextHolderStrategy; |
| 6 | import cn.iocoder.yudao.framework.security.core.filter.TokenAuthenticationFilter; | 5 | import cn.iocoder.yudao.framework.security.core.filter.TokenAuthenticationFilter; |
| 7 | import cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl; | 6 | import cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl; |
| 8 | import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl; | 7 | import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl; |
| 9 | -import cn.iocoder.yudao.framework.security.core.handler.LogoutSuccessHandlerImpl; | ||
| 10 | -import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService; | ||
| 11 | -import cn.iocoder.yudao.framework.web.config.WebProperties; | ||
| 12 | import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; | 8 | import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; |
| 9 | +import cn.iocoder.yudao.module.system.api.auth.OAuth2TokenApi; | ||
| 13 | import org.springframework.beans.factory.config.MethodInvokingFactoryBean; | 10 | import org.springframework.beans.factory.config.MethodInvokingFactoryBean; |
| 14 | import org.springframework.boot.context.properties.EnableConfigurationProperties; | 11 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| 15 | import org.springframework.context.annotation.Bean; | 12 | import org.springframework.context.annotation.Bean; |
| @@ -19,10 +16,8 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | @@ -19,10 +16,8 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||
| 19 | import org.springframework.security.crypto.password.PasswordEncoder; | 16 | import org.springframework.security.crypto.password.PasswordEncoder; |
| 20 | import org.springframework.security.web.AuthenticationEntryPoint; | 17 | import org.springframework.security.web.AuthenticationEntryPoint; |
| 21 | import org.springframework.security.web.access.AccessDeniedHandler; | 18 | import org.springframework.security.web.access.AccessDeniedHandler; |
| 22 | -import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; | ||
| 23 | 19 | ||
| 24 | import javax.annotation.Resource; | 20 | import javax.annotation.Resource; |
| 25 | -import java.util.List; | ||
| 26 | 21 | ||
| 27 | /** | 22 | /** |
| 28 | * Spring Security 自动配置类,主要用于相关组件的配置 | 23 | * Spring Security 自动配置类,主要用于相关组件的配置 |
| @@ -64,14 +59,6 @@ public class YudaoSecurityAutoConfiguration { | @@ -64,14 +59,6 @@ public class YudaoSecurityAutoConfiguration { | ||
| 64 | } | 59 | } |
| 65 | 60 | ||
| 66 | /** | 61 | /** |
| 67 | - * 退出处理类 Bean | ||
| 68 | - */ | ||
| 69 | - @Bean | ||
| 70 | - public LogoutSuccessHandler logoutSuccessHandler(MultiUserDetailsAuthenticationProvider authenticationProvider) { | ||
| 71 | - return new LogoutSuccessHandlerImpl(securityProperties, authenticationProvider); | ||
| 72 | - } | ||
| 73 | - | ||
| 74 | - /** | ||
| 75 | * Spring Security 加密器 | 62 | * Spring Security 加密器 |
| 76 | * 考虑到安全性,这里采用 BCryptPasswordEncoder 加密器 | 63 | * 考虑到安全性,这里采用 BCryptPasswordEncoder 加密器 |
| 77 | * | 64 | * |
| @@ -86,19 +73,9 @@ public class YudaoSecurityAutoConfiguration { | @@ -86,19 +73,9 @@ public class YudaoSecurityAutoConfiguration { | ||
| 86 | * Token 认证过滤器 Bean | 73 | * Token 认证过滤器 Bean |
| 87 | */ | 74 | */ |
| 88 | @Bean | 75 | @Bean |
| 89 | - public TokenAuthenticationFilter authenticationTokenFilter(MultiUserDetailsAuthenticationProvider authenticationProvider, | ||
| 90 | - GlobalExceptionHandler globalExceptionHandler) { | ||
| 91 | - return new TokenAuthenticationFilter(securityProperties, authenticationProvider, globalExceptionHandler); | ||
| 92 | - } | ||
| 93 | - | ||
| 94 | - /** | ||
| 95 | - * 身份验证的 Provider Bean,通过它实现账号 + 密码的认证 | ||
| 96 | - */ | ||
| 97 | - @Bean | ||
| 98 | - public MultiUserDetailsAuthenticationProvider authenticationProvider( | ||
| 99 | - List<SecurityAuthFrameworkService> securityFrameworkServices, | ||
| 100 | - WebProperties webProperties, PasswordEncoder passwordEncoder) { | ||
| 101 | - return new MultiUserDetailsAuthenticationProvider(securityFrameworkServices, webProperties, passwordEncoder); | 76 | + public TokenAuthenticationFilter authenticationTokenFilter(GlobalExceptionHandler globalExceptionHandler, |
| 77 | + OAuth2TokenApi oauth2TokenApi) { | ||
| 78 | + return new TokenAuthenticationFilter(securityProperties, globalExceptionHandler, oauth2TokenApi); | ||
| 102 | } | 79 | } |
| 103 | 80 | ||
| 104 | /** | 81 | /** |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
| 1 | package cn.iocoder.yudao.framework.security.config; | 1 | package cn.iocoder.yudao.framework.security.config; |
| 2 | 2 | ||
| 3 | -import cn.hutool.core.util.StrUtil; | ||
| 4 | -import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider; | ||
| 5 | import cn.iocoder.yudao.framework.security.core.filter.TokenAuthenticationFilter; | 3 | import cn.iocoder.yudao.framework.security.core.filter.TokenAuthenticationFilter; |
| 6 | import cn.iocoder.yudao.framework.web.config.WebProperties; | 4 | import cn.iocoder.yudao.framework.web.config.WebProperties; |
| 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
| @@ -9,7 +7,6 @@ import org.springframework.context.annotation.Bean; | @@ -9,7 +7,6 @@ import org.springframework.context.annotation.Bean; | ||
| 9 | import org.springframework.context.annotation.Configuration; | 7 | import org.springframework.context.annotation.Configuration; |
| 10 | import org.springframework.http.HttpMethod; | 8 | import org.springframework.http.HttpMethod; |
| 11 | import org.springframework.security.authentication.AuthenticationManager; | 9 | import org.springframework.security.authentication.AuthenticationManager; |
| 12 | -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; | ||
| 13 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; | 10 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; |
| 14 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; | 11 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
| 15 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; | 12 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; |
| @@ -17,7 +14,6 @@ import org.springframework.security.config.http.SessionCreationPolicy; | @@ -17,7 +14,6 @@ import org.springframework.security.config.http.SessionCreationPolicy; | ||
| 17 | import org.springframework.security.web.AuthenticationEntryPoint; | 14 | import org.springframework.security.web.AuthenticationEntryPoint; |
| 18 | import org.springframework.security.web.access.AccessDeniedHandler; | 15 | import org.springframework.security.web.access.AccessDeniedHandler; |
| 19 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | 16 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; |
| 20 | -import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; | ||
| 21 | 17 | ||
| 22 | import javax.annotation.Resource; | 18 | import javax.annotation.Resource; |
| 23 | import java.util.List; | 19 | import java.util.List; |
| @@ -34,8 +30,6 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | @@ -34,8 +30,6 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | ||
| 34 | @Resource | 30 | @Resource |
| 35 | private WebProperties webProperties; | 31 | private WebProperties webProperties; |
| 36 | 32 | ||
| 37 | - @Resource | ||
| 38 | - private MultiUserDetailsAuthenticationProvider authenticationProvider; | ||
| 39 | /** | 33 | /** |
| 40 | * 认证失败处理类 Bean | 34 | * 认证失败处理类 Bean |
| 41 | */ | 35 | */ |
| @@ -47,11 +41,6 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | @@ -47,11 +41,6 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | ||
| 47 | @Resource | 41 | @Resource |
| 48 | private AccessDeniedHandler accessDeniedHandler; | 42 | private AccessDeniedHandler accessDeniedHandler; |
| 49 | /** | 43 | /** |
| 50 | - * 退出处理类 Bean | ||
| 51 | - */ | ||
| 52 | - @Resource | ||
| 53 | - private LogoutSuccessHandler logoutSuccessHandler; | ||
| 54 | - /** | ||
| 55 | * Token 认证过滤器 Bean | 44 | * Token 认证过滤器 Bean |
| 56 | */ | 45 | */ |
| 57 | @Resource | 46 | @Resource |
| @@ -77,14 +66,6 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | @@ -77,14 +66,6 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | ||
| 77 | } | 66 | } |
| 78 | 67 | ||
| 79 | /** | 68 | /** |
| 80 | - * 身份认证接口 | ||
| 81 | - */ | ||
| 82 | - @Override | ||
| 83 | - protected void configure(AuthenticationManagerBuilder auth) throws Exception { | ||
| 84 | - auth.authenticationProvider(authenticationProvider); | ||
| 85 | - } | ||
| 86 | - | ||
| 87 | - /** | ||
| 88 | * 配置 URL 的安全配置 | 69 | * 配置 URL 的安全配置 |
| 89 | * | 70 | * |
| 90 | * anyRequest | 匹配所有请求路径 | 71 | * anyRequest | 匹配所有请求路径 |
| @@ -114,11 +95,8 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | @@ -114,11 +95,8 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | ||
| 114 | .headers().frameOptions().disable().and() | 95 | .headers().frameOptions().disable().and() |
| 115 | // 一堆自定义的 Spring Security 处理器 | 96 | // 一堆自定义的 Spring Security 处理器 |
| 116 | .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint) | 97 | .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint) |
| 117 | - .accessDeniedHandler(accessDeniedHandler).and() | ||
| 118 | - // 登出地址的配置 | ||
| 119 | - .logout().logoutSuccessHandler(logoutSuccessHandler).logoutRequestMatcher(request -> // 匹配多种用户类型的登出 | ||
| 120 | - StrUtil.equalsAny(request.getRequestURI(), buildAdminApi("/system/logout"), | ||
| 121 | - buildAppApi("/member/logout"))); | 98 | + .accessDeniedHandler(accessDeniedHandler); |
| 99 | + // 登录、登录暂时不使用 Spring Security 的拓展点,主要考虑一方面拓展多用户、多种登录方式相对复杂,一方面用户的学习成本较高 | ||
| 122 | 100 | ||
| 123 | // 设置每个请求的权限 | 101 | // 设置每个请求的权限 |
| 124 | httpSecurity | 102 | httpSecurity |
| @@ -140,11 +118,7 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | @@ -140,11 +118,7 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap | ||
| 140 | // 添加 JWT Filter | 118 | // 添加 JWT Filter |
| 141 | httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); | 119 | httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); |
| 142 | } | 120 | } |
| 143 | - | ||
| 144 | - private String buildAdminApi(String url) { | ||
| 145 | - return webProperties.getAdminApi().getPrefix() + url; | ||
| 146 | - } | ||
| 147 | - | 121 | + |
| 148 | private String buildAppApi(String url) { | 122 | private String buildAppApi(String url) { |
| 149 | return webProperties.getAppApi().getPrefix() + url; | 123 | return webProperties.getAppApi().getPrefix() + url; |
| 150 | } | 124 | } |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/LoginUser.java
| 1 | package cn.iocoder.yudao.framework.security.core; | 1 | package cn.iocoder.yudao.framework.security.core; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.map.MapUtil; | 3 | import cn.hutool.core.map.MapUtil; |
| 4 | -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 5 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | 4 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 6 | import com.fasterxml.jackson.annotation.JsonIgnore; | 5 | import com.fasterxml.jackson.annotation.JsonIgnore; |
| 7 | import lombok.Data; | 6 | import lombok.Data; |
| 8 | -import org.springframework.security.core.GrantedAuthority; | ||
| 9 | -import org.springframework.security.core.userdetails.UserDetails; | ||
| 10 | 7 | ||
| 11 | -import java.util.*; | 8 | +import java.util.HashMap; |
| 9 | +import java.util.Map; | ||
| 12 | 10 | ||
| 13 | /** | 11 | /** |
| 14 | * 登录用户信息 | 12 | * 登录用户信息 |
| @@ -16,7 +14,7 @@ import java.util.*; | @@ -16,7 +14,7 @@ import java.util.*; | ||
| 16 | * @author 芋道源码 | 14 | * @author 芋道源码 |
| 17 | */ | 15 | */ |
| 18 | @Data | 16 | @Data |
| 19 | -public class LoginUser implements UserDetails { | 17 | +public class LoginUser { |
| 20 | 18 | ||
| 21 | /** | 19 | /** |
| 22 | * 用户编号 | 20 | * 用户编号 |
| @@ -29,38 +27,10 @@ public class LoginUser implements UserDetails { | @@ -29,38 +27,10 @@ public class LoginUser implements UserDetails { | ||
| 29 | */ | 27 | */ |
| 30 | private Integer userType; | 28 | private Integer userType; |
| 31 | /** | 29 | /** |
| 32 | - * 最后更新时间 | ||
| 33 | - */ | ||
| 34 | - private Date updateTime; | ||
| 35 | - | ||
| 36 | - /** | ||
| 37 | - * 用户名 | ||
| 38 | - */ | ||
| 39 | - private String username; | ||
| 40 | - /** | ||
| 41 | - * 密码 | ||
| 42 | - */ | ||
| 43 | - private String password; | ||
| 44 | - /** | ||
| 45 | - * 状态 | ||
| 46 | - */ | ||
| 47 | - private Integer status; | ||
| 48 | - /** | ||
| 49 | * 租户编号 | 30 | * 租户编号 |
| 50 | */ | 31 | */ |
| 51 | private Long tenantId; | 32 | private Long tenantId; |
| 52 | 33 | ||
| 53 | - // ========== UserTypeEnum.ADMIN 独有字段 ========== | ||
| 54 | - // TODO 芋艿:可以通过定义一个 Map<String, String> exts 的方式,去除管理员的字段。不过这样会导致系统比较复杂,所以暂时不去掉先; | ||
| 55 | - /** | ||
| 56 | - * 角色编号数组 | ||
| 57 | - */ | ||
| 58 | - private Set<Long> roleIds; | ||
| 59 | - /** | ||
| 60 | - * 部门编号 | ||
| 61 | - */ | ||
| 62 | - private Long deptId; | ||
| 63 | - | ||
| 64 | // ========== 上下文 ========== | 34 | // ========== 上下文 ========== |
| 65 | /** | 35 | /** |
| 66 | * 上下文字段,不进行持久化 | 36 | * 上下文字段,不进行持久化 |
| @@ -70,49 +40,6 @@ public class LoginUser implements UserDetails { | @@ -70,49 +40,6 @@ public class LoginUser implements UserDetails { | ||
| 70 | @JsonIgnore | 40 | @JsonIgnore |
| 71 | private Map<String, Object> context; | 41 | private Map<String, Object> context; |
| 72 | 42 | ||
| 73 | - @Override | ||
| 74 | - @JsonIgnore// 避免序列化 | ||
| 75 | - public String getPassword() { | ||
| 76 | - return password; | ||
| 77 | - } | ||
| 78 | - | ||
| 79 | - @Override | ||
| 80 | - public String getUsername() { | ||
| 81 | - return username; | ||
| 82 | - } | ||
| 83 | - | ||
| 84 | - @Override | ||
| 85 | - @JsonIgnore// 避免序列化 | ||
| 86 | - public boolean isEnabled() { | ||
| 87 | - return CommonStatusEnum.ENABLE.getStatus().equals(status); | ||
| 88 | - } | ||
| 89 | - | ||
| 90 | - @Override | ||
| 91 | - @JsonIgnore// 避免序列化 | ||
| 92 | - public Collection<? extends GrantedAuthority> getAuthorities() { | ||
| 93 | - return new HashSet<>(); | ||
| 94 | - } | ||
| 95 | - | ||
| 96 | - @Override | ||
| 97 | - @JsonIgnore// 避免序列化 | ||
| 98 | - public boolean isAccountNonExpired() { | ||
| 99 | - return true; // 返回 true,不依赖 Spring Security 判断 | ||
| 100 | - } | ||
| 101 | - | ||
| 102 | - @Override | ||
| 103 | - @JsonIgnore// 避免序列化 | ||
| 104 | - public boolean isAccountNonLocked() { | ||
| 105 | - return true; // 返回 true,不依赖 Spring Security 判断 | ||
| 106 | - } | ||
| 107 | - | ||
| 108 | - @Override | ||
| 109 | - @JsonIgnore// 避免序列化 | ||
| 110 | - public boolean isCredentialsNonExpired() { | ||
| 111 | - return true; // 返回 true,不依赖 Spring Security 判断 | ||
| 112 | - } | ||
| 113 | - | ||
| 114 | - // ========== 上下文 ========== | ||
| 115 | - | ||
| 116 | public void setContext(String key, Object value) { | 43 | public void setContext(String key, Object value) { |
| 117 | if (context == null) { | 44 | if (context == null) { |
| 118 | context = new HashMap<>(); | 45 | context = new HashMap<>(); |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/MultiUserDetailsAuthenticationProvider.java deleted
| 1 | -package cn.iocoder.yudao.framework.security.core.authentication; | ||
| 2 | - | ||
| 3 | -import cn.hutool.core.lang.Assert; | ||
| 4 | -import cn.hutool.core.util.StrUtil; | ||
| 5 | -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||
| 6 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 7 | -import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService; | ||
| 8 | -import cn.iocoder.yudao.framework.web.config.WebProperties; | ||
| 9 | -import org.springframework.security.authentication.AuthenticationProvider; | ||
| 10 | -import org.springframework.security.authentication.BadCredentialsException; | ||
| 11 | -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
| 12 | -import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider; | ||
| 13 | -import org.springframework.security.core.AuthenticationException; | ||
| 14 | -import org.springframework.security.core.userdetails.UserDetails; | ||
| 15 | -import org.springframework.security.crypto.password.PasswordEncoder; | ||
| 16 | - | ||
| 17 | -import javax.servlet.http.HttpServletRequest; | ||
| 18 | -import java.util.HashMap; | ||
| 19 | -import java.util.List; | ||
| 20 | -import java.util.Map; | ||
| 21 | - | ||
| 22 | -/** | ||
| 23 | - * 支持多用户类型的 AuthenticationProvider 实现类 | ||
| 24 | - * | ||
| 25 | - * 为什么不用 {@link org.springframework.security.authentication.ProviderManager} 呢? | ||
| 26 | - * 原因是,需要每个用户类型实现对应的 {@link AuthenticationProvider} + authentication,略显麻烦。实际,也是可以实现的。 | ||
| 27 | - * | ||
| 28 | - * 另外,额外支持 verifyTokenAndRefresh 校验令牌、logout 登出、mockLogin 模拟登陆等操作。 | ||
| 29 | - * 实际上,它就是 {@link SecurityAuthFrameworkService} 定义的三个接口。 | ||
| 30 | - * 因为需要支持多种类型,所以需要根据请求的 URL,判断出对应的用户类型,从而使用对应的 SecurityAuthFrameworkService 是吸纳 | ||
| 31 | - * | ||
| 32 | - * @see cn.iocoder.yudao.framework.common.enums.UserTypeEnum | ||
| 33 | - * @author 芋道源码 | ||
| 34 | - */ | ||
| 35 | -public class MultiUserDetailsAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { | ||
| 36 | - | ||
| 37 | - private final Map<UserTypeEnum, SecurityAuthFrameworkService> services = new HashMap<>(); | ||
| 38 | - | ||
| 39 | - private final WebProperties properties; | ||
| 40 | - | ||
| 41 | - private final PasswordEncoder passwordEncoder; | ||
| 42 | - | ||
| 43 | - public MultiUserDetailsAuthenticationProvider(List<SecurityAuthFrameworkService> serviceList, | ||
| 44 | - WebProperties properties, PasswordEncoder passwordEncoder) { | ||
| 45 | - serviceList.forEach(service -> services.put(service.getUserType(), service)); | ||
| 46 | - this.properties = properties; | ||
| 47 | - this.passwordEncoder = passwordEncoder; | ||
| 48 | - } | ||
| 49 | - | ||
| 50 | - // ========== AuthenticationProvider 相关 ========== | ||
| 51 | - | ||
| 52 | - @Override | ||
| 53 | - protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) | ||
| 54 | - throws AuthenticationException { | ||
| 55 | - // 执行用户的加载 | ||
| 56 | - return selectService(authentication).loadUserByUsername(username); | ||
| 57 | - } | ||
| 58 | - | ||
| 59 | - private SecurityAuthFrameworkService selectService(UsernamePasswordAuthenticationToken authentication) { | ||
| 60 | - // 第一步,获得用户类型 | ||
| 61 | - UserTypeEnum userType = getUserType(authentication); | ||
| 62 | - // 第二步,获得 SecurityAuthFrameworkService | ||
| 63 | - SecurityAuthFrameworkService service = services.get(userType); | ||
| 64 | - Assert.notNull(service, "用户类型({}) 找不到 SecurityAuthFrameworkService 实现类", userType); | ||
| 65 | - return service; | ||
| 66 | - } | ||
| 67 | - | ||
| 68 | - private UserTypeEnum getUserType(UsernamePasswordAuthenticationToken authentication) { | ||
| 69 | - Assert.isInstanceOf(MultiUsernamePasswordAuthenticationToken.class, authentication); | ||
| 70 | - MultiUsernamePasswordAuthenticationToken multiAuthentication = (MultiUsernamePasswordAuthenticationToken) authentication; | ||
| 71 | - UserTypeEnum userType = multiAuthentication.getUserType(); | ||
| 72 | - Assert.notNull(userType, "用户类型不能为空"); | ||
| 73 | - return userType; | ||
| 74 | - } | ||
| 75 | - | ||
| 76 | - @Override // copy 自 DaoAuthenticationProvider 的 additionalAuthenticationChecks 方法 | ||
| 77 | - protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) | ||
| 78 | - throws AuthenticationException { | ||
| 79 | - // 校验 credentials | ||
| 80 | - if (authentication.getCredentials() == null) { | ||
| 81 | - this.logger.debug("Failed to authenticate since no credentials provided"); | ||
| 82 | - throw new BadCredentialsException(this.messages.getMessage( | ||
| 83 | - "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); | ||
| 84 | - } | ||
| 85 | - // 校验 password | ||
| 86 | - String presentedPassword = authentication.getCredentials().toString(); | ||
| 87 | - if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { | ||
| 88 | - this.logger.debug("Failed to authenticate since password does not match stored value"); | ||
| 89 | - throw new BadCredentialsException(this.messages.getMessage( | ||
| 90 | - "AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); | ||
| 91 | - } | ||
| 92 | - } | ||
| 93 | - | ||
| 94 | - // ========== SecurityAuthFrameworkService 相关 ========== | ||
| 95 | - | ||
| 96 | - /** | ||
| 97 | - * 校验 token 的有效性,并获取用户信息 | ||
| 98 | - * 通过后,刷新 token 的过期时间 | ||
| 99 | - * | ||
| 100 | - * @param request 请求 | ||
| 101 | - * @param token token | ||
| 102 | - * @return 用户信息 | ||
| 103 | - */ | ||
| 104 | - public LoginUser verifyTokenAndRefresh(HttpServletRequest request, String token) { | ||
| 105 | - return selectService(request).verifyTokenAndRefresh(token); | ||
| 106 | - } | ||
| 107 | - | ||
| 108 | - /** | ||
| 109 | - * 模拟指定用户编号的 LoginUser | ||
| 110 | - * | ||
| 111 | - * @param request 请求 | ||
| 112 | - * @param userId 用户编号 | ||
| 113 | - * @return 登录用户 | ||
| 114 | - */ | ||
| 115 | - public LoginUser mockLogin(HttpServletRequest request, Long userId) { | ||
| 116 | - return selectService(request).mockLogin(userId); | ||
| 117 | - } | ||
| 118 | - | ||
| 119 | - /** | ||
| 120 | - * 基于 token 退出登录 | ||
| 121 | - * | ||
| 122 | - * @param request 请求 | ||
| 123 | - * @param token token | ||
| 124 | - */ | ||
| 125 | - public void logout(HttpServletRequest request, String token) { | ||
| 126 | - selectService(request).logout(token); | ||
| 127 | - } | ||
| 128 | - | ||
| 129 | - private SecurityAuthFrameworkService selectService(HttpServletRequest request) { | ||
| 130 | - // 第一步,获得用户类型 | ||
| 131 | - UserTypeEnum userType = getUserType(request); | ||
| 132 | - // 第二步,获得 SecurityAuthFrameworkService | ||
| 133 | - SecurityAuthFrameworkService service = services.get(userType); | ||
| 134 | - Assert.notNull(service, "URI({}) 用户类型({}) 找不到 SecurityAuthFrameworkService 实现类", | ||
| 135 | - request.getRequestURI(), userType); | ||
| 136 | - return service; | ||
| 137 | - } | ||
| 138 | - | ||
| 139 | - private UserTypeEnum getUserType(HttpServletRequest request) { | ||
| 140 | - if (request.getRequestURI().startsWith(properties.getAdminApi().getPrefix())) { | ||
| 141 | - return UserTypeEnum.ADMIN; | ||
| 142 | - } | ||
| 143 | - if (request.getRequestURI().startsWith(properties.getAppApi().getPrefix())) { | ||
| 144 | - return UserTypeEnum.MEMBER; | ||
| 145 | - } | ||
| 146 | - throw new IllegalArgumentException(StrUtil.format("URI({}) 找不到匹配的用户类型", request.getRequestURI())); | ||
| 147 | - } | ||
| 148 | - | ||
| 149 | -} |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/authentication/MultiUsernamePasswordAuthenticationToken.java deleted
| 1 | -package cn.iocoder.yudao.framework.security.core.authentication; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||
| 4 | -import lombok.Getter; | ||
| 5 | -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
| 6 | -import org.springframework.security.core.GrantedAuthority; | ||
| 7 | - | ||
| 8 | -import java.util.Collection; | ||
| 9 | - | ||
| 10 | -/** | ||
| 11 | - * 支持多用户的 UsernamePasswordAuthenticationToken 实现类 | ||
| 12 | - * | ||
| 13 | - * @author 芋道源码 | ||
| 14 | - */ | ||
| 15 | -@Getter | ||
| 16 | -public class MultiUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken { | ||
| 17 | - | ||
| 18 | - /** | ||
| 19 | - * 用户类型 | ||
| 20 | - */ | ||
| 21 | - private UserTypeEnum userType; | ||
| 22 | - | ||
| 23 | - public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials) { | ||
| 24 | - super(principal, credentials); | ||
| 25 | - } | ||
| 26 | - | ||
| 27 | - public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials, | ||
| 28 | - Collection<? extends GrantedAuthority> authorities) { | ||
| 29 | - super(principal, credentials, authorities); | ||
| 30 | - } | ||
| 31 | - | ||
| 32 | - public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials, UserTypeEnum userType) { | ||
| 33 | - super(principal, credentials); | ||
| 34 | - this.userType = userType; | ||
| 35 | - } | ||
| 36 | - | ||
| 37 | - public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials, | ||
| 38 | - Collection<? extends GrantedAuthority> authorities, UserTypeEnum userType) { | ||
| 39 | - super(principal, credentials, authorities); | ||
| 40 | - this.userType = userType; | ||
| 41 | - } | ||
| 42 | - | ||
| 43 | -} |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/filter/TokenAuthenticationFilter.java
| 1 | package cn.iocoder.yudao.framework.security.core.filter; | 1 | package cn.iocoder.yudao.framework.security.core.filter; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.util.ObjectUtil; | ||
| 3 | import cn.hutool.core.util.StrUtil; | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | +import cn.iocoder.yudao.framework.common.exception.ServiceException; | ||
| 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 6 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; | 7 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; |
| 6 | import cn.iocoder.yudao.framework.security.config.SecurityProperties; | 8 | import cn.iocoder.yudao.framework.security.config.SecurityProperties; |
| 7 | import cn.iocoder.yudao.framework.security.core.LoginUser; | 9 | import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 8 | -import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider; | ||
| 9 | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; | 10 | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; |
| 10 | import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; | 11 | import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; |
| 12 | +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; | ||
| 13 | +import cn.iocoder.yudao.module.system.api.auth.OAuth2TokenApi; | ||
| 14 | +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO; | ||
| 11 | import lombok.RequiredArgsConstructor; | 15 | import lombok.RequiredArgsConstructor; |
| 16 | +import org.springframework.security.access.AccessDeniedException; | ||
| 12 | import org.springframework.web.filter.OncePerRequestFilter; | 17 | import org.springframework.web.filter.OncePerRequestFilter; |
| 13 | 18 | ||
| 14 | import javax.servlet.FilterChain; | 19 | import javax.servlet.FilterChain; |
| @@ -28,24 +33,26 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { | @@ -28,24 +33,26 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { | ||
| 28 | 33 | ||
| 29 | private final SecurityProperties securityProperties; | 34 | private final SecurityProperties securityProperties; |
| 30 | 35 | ||
| 31 | - private final MultiUserDetailsAuthenticationProvider authenticationProvider; | ||
| 32 | - | ||
| 33 | private final GlobalExceptionHandler globalExceptionHandler; | 36 | private final GlobalExceptionHandler globalExceptionHandler; |
| 34 | 37 | ||
| 38 | + private final OAuth2TokenApi oauth2TokenApi; | ||
| 39 | + | ||
| 35 | @Override | 40 | @Override |
| 36 | @SuppressWarnings("NullableProblems") | 41 | @SuppressWarnings("NullableProblems") |
| 37 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) | 42 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) |
| 38 | throws ServletException, IOException { | 43 | throws ServletException, IOException { |
| 39 | String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader()); | 44 | String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader()); |
| 40 | if (StrUtil.isNotEmpty(token)) { | 45 | if (StrUtil.isNotEmpty(token)) { |
| 46 | + Integer userType = WebFrameworkUtils.getLoginUserType(request); | ||
| 41 | try { | 47 | try { |
| 42 | - // 验证 token 有效性 | ||
| 43 | - LoginUser loginUser = authenticationProvider.verifyTokenAndRefresh(request, token); | ||
| 44 | - // 模拟 Login 功能,方便日常开发调试 | 48 | + // 1.1 基于 token 构建登录用户 |
| 49 | + LoginUser loginUser = buildLoginUserByToken(token, userType); | ||
| 50 | + // 1.2 模拟 Login 功能,方便日常开发调试 | ||
| 45 | if (loginUser == null) { | 51 | if (loginUser == null) { |
| 46 | - loginUser = mockLoginUser(request, token); | 52 | + loginUser = mockLoginUser(request, token, userType); |
| 47 | } | 53 | } |
| 48 | - // 设置当前用户 | 54 | + |
| 55 | + // 2. 设置当前用户 | ||
| 49 | if (loginUser != null) { | 56 | if (loginUser != null) { |
| 50 | SecurityFrameworkUtils.setLoginUser(loginUser, request); | 57 | SecurityFrameworkUtils.setLoginUser(loginUser, request); |
| 51 | } | 58 | } |
| @@ -60,6 +67,25 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { | @@ -60,6 +67,25 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { | ||
| 60 | chain.doFilter(request, response); | 67 | chain.doFilter(request, response); |
| 61 | } | 68 | } |
| 62 | 69 | ||
| 70 | + private LoginUser buildLoginUserByToken(String token, Integer userType) { | ||
| 71 | + try { | ||
| 72 | + OAuth2AccessTokenCheckRespDTO accessToken = oauth2TokenApi.checkAccessToken(token); | ||
| 73 | + if (accessToken == null) { | ||
| 74 | + return null; | ||
| 75 | + } | ||
| 76 | + // 用户类型不匹配,无权限 | ||
| 77 | + if (ObjectUtil.notEqual(accessToken.getUserType(), userType)) { | ||
| 78 | + throw new AccessDeniedException("错误的用户类型"); | ||
| 79 | + } | ||
| 80 | + // 构建登录用户 | ||
| 81 | + return new LoginUser().setId(accessToken.getUserId()).setUserType(accessToken.getUserType()) | ||
| 82 | + .setTenantId(accessToken.getTenantId()); | ||
| 83 | + } catch (ServiceException serviceException) { | ||
| 84 | + // 校验 Token 不通过时,考虑到一些接口是无需登录的,所以直接返回 null 即可 | ||
| 85 | + return null; | ||
| 86 | + } | ||
| 87 | + } | ||
| 88 | + | ||
| 63 | /** | 89 | /** |
| 64 | * 模拟登录用户,方便日常开发调试 | 90 | * 模拟登录用户,方便日常开发调试 |
| 65 | * | 91 | * |
| @@ -67,9 +93,10 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { | @@ -67,9 +93,10 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { | ||
| 67 | * | 93 | * |
| 68 | * @param request 请求 | 94 | * @param request 请求 |
| 69 | * @param token 模拟的 token,格式为 {@link SecurityProperties#getMockSecret()} + 用户编号 | 95 | * @param token 模拟的 token,格式为 {@link SecurityProperties#getMockSecret()} + 用户编号 |
| 96 | + * @param userType 用户类型 | ||
| 70 | * @return 模拟的 LoginUser | 97 | * @return 模拟的 LoginUser |
| 71 | */ | 98 | */ |
| 72 | - private LoginUser mockLoginUser(HttpServletRequest request, String token) { | 99 | + private LoginUser mockLoginUser(HttpServletRequest request, String token, Integer userType) { |
| 73 | if (!securityProperties.getMockEnable()) { | 100 | if (!securityProperties.getMockEnable()) { |
| 74 | return null; | 101 | return null; |
| 75 | } | 102 | } |
| @@ -77,8 +104,10 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { | @@ -77,8 +104,10 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter { | ||
| 77 | if (!token.startsWith(securityProperties.getMockSecret())) { | 104 | if (!token.startsWith(securityProperties.getMockSecret())) { |
| 78 | return null; | 105 | return null; |
| 79 | } | 106 | } |
| 107 | + // 构建模拟用户 | ||
| 80 | Long userId = Long.valueOf(token.substring(securityProperties.getMockSecret().length())); | 108 | Long userId = Long.valueOf(token.substring(securityProperties.getMockSecret().length())); |
| 81 | - return authenticationProvider.mockLogin(request, userId); | 109 | + return new LoginUser().setId(userId).setUserType(userType) |
| 110 | + .setTenantId(WebFrameworkUtils.getTenantId(request)); | ||
| 82 | } | 111 | } |
| 83 | 112 | ||
| 84 | } | 113 | } |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/handler/LogoutSuccessHandlerImpl.java deleted
| 1 | -package cn.iocoder.yudao.framework.security.core.handler; | ||
| 2 | - | ||
| 3 | -import cn.hutool.core.util.StrUtil; | ||
| 4 | -import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 5 | -import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; | ||
| 6 | -import cn.iocoder.yudao.framework.security.config.SecurityProperties; | ||
| 7 | -import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider; | ||
| 8 | -import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; | ||
| 9 | -import lombok.AllArgsConstructor; | ||
| 10 | -import org.springframework.security.core.Authentication; | ||
| 11 | -import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; | ||
| 12 | - | ||
| 13 | -import javax.servlet.http.HttpServletRequest; | ||
| 14 | -import javax.servlet.http.HttpServletResponse; | ||
| 15 | - | ||
| 16 | - | ||
| 17 | -/** | ||
| 18 | - * 自定义退出处理器 | ||
| 19 | - * | ||
| 20 | - * @author ruoyi | ||
| 21 | - */ | ||
| 22 | -@AllArgsConstructor | ||
| 23 | -public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { | ||
| 24 | - | ||
| 25 | - private final SecurityProperties securityProperties; | ||
| 26 | - | ||
| 27 | - private final MultiUserDetailsAuthenticationProvider authenticationProvider; | ||
| 28 | - | ||
| 29 | - @Override | ||
| 30 | - public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { | ||
| 31 | - // 执行退出 | ||
| 32 | - String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader()); | ||
| 33 | - if (StrUtil.isNotBlank(token)) { | ||
| 34 | - authenticationProvider.logout(request, token); | ||
| 35 | - } | ||
| 36 | - // 返回成功 | ||
| 37 | - ServletUtils.writeJSON(response, CommonResult.success(null)); | ||
| 38 | - } | ||
| 39 | - | ||
| 40 | -} |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/service/SecurityAuthFrameworkService.java deleted
| 1 | -package cn.iocoder.yudao.framework.security.core.service; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||
| 4 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 5 | -import org.springframework.security.core.userdetails.UserDetailsService; | ||
| 6 | - | ||
| 7 | -/** | ||
| 8 | - * Security 框架 Auth Service 接口,定义不同用户类型的 {@link UserTypeEnum} 需要实现的方法 | ||
| 9 | - * | ||
| 10 | - * @author 芋道源码 | ||
| 11 | - */ | ||
| 12 | -public interface SecurityAuthFrameworkService extends UserDetailsService { | ||
| 13 | - | ||
| 14 | - /** | ||
| 15 | - * 校验 token 的有效性,并获取用户信息 | ||
| 16 | - * 通过后,刷新 token 的过期时间 | ||
| 17 | - * | ||
| 18 | - * @param token token | ||
| 19 | - * @return 用户信息 | ||
| 20 | - */ | ||
| 21 | - LoginUser verifyTokenAndRefresh(String token); | ||
| 22 | - | ||
| 23 | - /** | ||
| 24 | - * 模拟指定用户编号的 LoginUser | ||
| 25 | - * | ||
| 26 | - * @param userId 用户编号 | ||
| 27 | - * @return 登录用户 | ||
| 28 | - */ | ||
| 29 | - LoginUser mockLogin(Long userId); | ||
| 30 | - | ||
| 31 | - /** | ||
| 32 | - * 基于 token 退出登录 | ||
| 33 | - * | ||
| 34 | - * @param token token | ||
| 35 | - */ | ||
| 36 | - void logout(String token); | ||
| 37 | - | ||
| 38 | - /** | ||
| 39 | - * 获得用户类型。每个用户类型,对应一个 SecurityAuthFrameworkService 实现类。 | ||
| 40 | - * | ||
| 41 | - * @return 用户类型 | ||
| 42 | - */ | ||
| 43 | - UserTypeEnum getUserType(); | ||
| 44 | - | ||
| 45 | -} |
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/core/util/SecurityFrameworkUtils.java
| @@ -11,7 +11,7 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS | @@ -11,7 +11,7 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS | ||
| 11 | import org.springframework.util.StringUtils; | 11 | import org.springframework.util.StringUtils; |
| 12 | 12 | ||
| 13 | import javax.servlet.http.HttpServletRequest; | 13 | import javax.servlet.http.HttpServletRequest; |
| 14 | -import java.util.Set; | 14 | +import java.util.Collections; |
| 15 | 15 | ||
| 16 | /** | 16 | /** |
| 17 | * 安全服务工具类 | 17 | * 安全服务工具类 |
| @@ -80,17 +80,6 @@ public class SecurityFrameworkUtils { | @@ -80,17 +80,6 @@ public class SecurityFrameworkUtils { | ||
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | /** | 82 | /** |
| 83 | - * 获得当前用户的角色编号数组 | ||
| 84 | - * | ||
| 85 | - * @return 角色编号数组 | ||
| 86 | - */ | ||
| 87 | - @Nullable | ||
| 88 | - public static Set<Long> getLoginUserRoleIds() { | ||
| 89 | - LoginUser loginUser = getLoginUser(); | ||
| 90 | - return loginUser != null ? loginUser.getRoleIds() : null; | ||
| 91 | - } | ||
| 92 | - | ||
| 93 | - /** | ||
| 94 | * 设置当前用户 | 83 | * 设置当前用户 |
| 95 | * | 84 | * |
| 96 | * @param loginUser 登录用户 | 85 | * @param loginUser 登录用户 |
| @@ -110,7 +99,7 @@ public class SecurityFrameworkUtils { | @@ -110,7 +99,7 @@ public class SecurityFrameworkUtils { | ||
| 110 | private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) { | 99 | private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) { |
| 111 | // 创建 UsernamePasswordAuthenticationToken 对象 | 100 | // 创建 UsernamePasswordAuthenticationToken 对象 |
| 112 | UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( | 101 | UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( |
| 113 | - loginUser, null, loginUser.getAuthorities()); | 102 | + loginUser, null, Collections.emptyList()); |
| 114 | authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); | 103 | authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); |
| 115 | return authenticationToken; | 104 | return authenticationToken; |
| 116 | } | 105 | } |
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java
| @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.web.core.filter.DemoFilter; | @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.web.core.filter.DemoFilter; | ||
| 7 | import cn.iocoder.yudao.framework.web.core.filter.XssFilter; | 7 | import cn.iocoder.yudao.framework.web.core.filter.XssFilter; |
| 8 | import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; | 8 | import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; |
| 9 | import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler; | 9 | import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler; |
| 10 | +import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; | ||
| 10 | import org.springframework.beans.factory.annotation.Value; | 11 | import org.springframework.beans.factory.annotation.Value; |
| 11 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | 12 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 12 | import org.springframework.boot.context.properties.EnableConfigurationProperties; | 13 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| @@ -65,6 +66,13 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer { | @@ -65,6 +66,13 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer { | ||
| 65 | return new GlobalResponseBodyHandler(); | 66 | return new GlobalResponseBodyHandler(); |
| 66 | } | 67 | } |
| 67 | 68 | ||
| 69 | + @Bean | ||
| 70 | + @SuppressWarnings("InstantiationOfUtilityClass") | ||
| 71 | + public WebFrameworkUtils webFrameworkUtils(WebProperties webProperties) { | ||
| 72 | + // 由于 WebFrameworkUtils 需要使用到 webProperties 属性,所以注册为一个 Bean | ||
| 73 | + return new WebFrameworkUtils(webProperties); | ||
| 74 | + } | ||
| 75 | + | ||
| 68 | // ========== Filter 相关 ========== | 76 | // ========== Filter 相关 ========== |
| 69 | 77 | ||
| 70 | /** | 78 | /** |
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/util/WebFrameworkUtils.java
| 1 | package cn.iocoder.yudao.framework.web.core.util; | 1 | package cn.iocoder.yudao.framework.web.core.util; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.util.StrUtil; | ||
| 3 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | 4 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 5 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 6 | +import cn.iocoder.yudao.framework.web.config.WebProperties; | ||
| 5 | import org.springframework.web.context.request.RequestAttributes; | 7 | import org.springframework.web.context.request.RequestAttributes; |
| 6 | import org.springframework.web.context.request.RequestContextHolder; | 8 | import org.springframework.web.context.request.RequestContextHolder; |
| 7 | import org.springframework.web.context.request.ServletRequestAttributes; | 9 | import org.springframework.web.context.request.ServletRequestAttributes; |
| @@ -21,16 +23,43 @@ public class WebFrameworkUtils { | @@ -21,16 +23,43 @@ public class WebFrameworkUtils { | ||
| 21 | 23 | ||
| 22 | private static final String REQUEST_ATTRIBUTE_COMMON_RESULT = "common_result"; | 24 | private static final String REQUEST_ATTRIBUTE_COMMON_RESULT = "common_result"; |
| 23 | 25 | ||
| 26 | + private static final String HEADER_TENANT_ID = "tenant-id"; | ||
| 27 | + | ||
| 28 | + private static WebProperties properties; | ||
| 29 | + | ||
| 30 | + public WebFrameworkUtils(WebProperties webProperties) { | ||
| 31 | + WebFrameworkUtils.properties = webProperties; | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 获得租户编号,从 header 中 | ||
| 36 | + * 考虑到其它 framework 组件也会使用到租户编号,所以不得不放在 WebFrameworkUtils 统一提供 | ||
| 37 | + * | ||
| 38 | + * @param request 请求 | ||
| 39 | + * @return 租户编号 | ||
| 40 | + */ | ||
| 41 | + public static Long getTenantId(HttpServletRequest request) { | ||
| 42 | + String tenantId = request.getHeader(HEADER_TENANT_ID); | ||
| 43 | + return StrUtil.isNotEmpty(tenantId) ? Long.valueOf(tenantId) : null; | ||
| 44 | + } | ||
| 45 | + | ||
| 24 | public static void setLoginUserId(ServletRequest request, Long userId) { | 46 | public static void setLoginUserId(ServletRequest request, Long userId) { |
| 25 | request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_ID, userId); | 47 | request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_ID, userId); |
| 26 | } | 48 | } |
| 27 | 49 | ||
| 50 | + /** | ||
| 51 | + * 设置用户类型 | ||
| 52 | + * | ||
| 53 | + * @param request 请求 | ||
| 54 | + * @param userType 用户类型 | ||
| 55 | + */ | ||
| 28 | public static void setLoginUserType(ServletRequest request, Integer userType) { | 56 | public static void setLoginUserType(ServletRequest request, Integer userType) { |
| 29 | request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_TYPE, userType); | 57 | request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_TYPE, userType); |
| 30 | } | 58 | } |
| 31 | 59 | ||
| 32 | /** | 60 | /** |
| 33 | * 获得当前用户的编号,从请求中 | 61 | * 获得当前用户的编号,从请求中 |
| 62 | + * 注意:该方法仅限于 framework 框架使用!!! | ||
| 34 | * | 63 | * |
| 35 | * @param request 请求 | 64 | * @param request 请求 |
| 36 | * @return 用户编号 | 65 | * @return 用户编号 |
| @@ -43,7 +72,8 @@ public class WebFrameworkUtils { | @@ -43,7 +72,8 @@ public class WebFrameworkUtils { | ||
| 43 | } | 72 | } |
| 44 | 73 | ||
| 45 | /** | 74 | /** |
| 46 | - * 获得当前用户的类型,从请求中 | 75 | + * 获得当前用户的类型 |
| 76 | + * 注意:该方法仅限于 web 相关的 framework 组件使用!!! | ||
| 47 | * | 77 | * |
| 48 | * @param request 请求 | 78 | * @param request 请求 |
| 49 | * @return 用户编号 | 79 | * @return 用户编号 |
| @@ -52,7 +82,19 @@ public class WebFrameworkUtils { | @@ -52,7 +82,19 @@ public class WebFrameworkUtils { | ||
| 52 | if (request == null) { | 82 | if (request == null) { |
| 53 | return null; | 83 | return null; |
| 54 | } | 84 | } |
| 55 | - return (Integer) request.getAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_TYPE); | 85 | + // 1. 优先,从 Attribute 中获取 |
| 86 | + Integer userType = (Integer) request.getAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_TYPE); | ||
| 87 | + if (userType != null) { | ||
| 88 | + return userType; | ||
| 89 | + } | ||
| 90 | + // 2. 其次,基于 URL 前缀的约定 | ||
| 91 | + if (request.getRequestURI().startsWith(properties.getAdminApi().getPrefix())) { | ||
| 92 | + return UserTypeEnum.ADMIN.getValue(); | ||
| 93 | + } | ||
| 94 | + if (request.getRequestURI().startsWith(properties.getAppApi().getPrefix())) { | ||
| 95 | + return UserTypeEnum.MEMBER.getValue(); | ||
| 96 | + } | ||
| 97 | + return null; | ||
| 56 | } | 98 | } |
| 57 | 99 | ||
| 58 | public static Integer getLoginUserType() { | 100 | public static Integer getLoginUserType() { |
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java
| @@ -4,6 +4,7 @@ import cn.hutool.core.io.IoUtil; | @@ -4,6 +4,7 @@ import cn.hutool.core.io.IoUtil; | ||
| 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; | 6 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; |
| 7 | +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; | ||
| 7 | import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO; | 8 | import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO; |
| 8 | import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileRespVO; | 9 | import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileRespVO; |
| 9 | import cn.iocoder.yudao.module.infra.convert.file.FileConvert; | 10 | import cn.iocoder.yudao.module.infra.convert.file.FileConvert; |
| @@ -42,8 +43,9 @@ public class FileController { | @@ -42,8 +43,9 @@ public class FileController { | ||
| 42 | @ApiImplicitParam(name = "file", value = "文件附件", required = true, dataTypeClass = MultipartFile.class), | 43 | @ApiImplicitParam(name = "file", value = "文件附件", required = true, dataTypeClass = MultipartFile.class), |
| 43 | @ApiImplicitParam(name = "path", value = "文件路径", example = "yudaoyuanma.png", dataTypeClass = String.class) | 44 | @ApiImplicitParam(name = "path", value = "文件路径", example = "yudaoyuanma.png", dataTypeClass = String.class) |
| 44 | }) | 45 | }) |
| 46 | + @OperateLog(logArgs = false) // 上传文件,没有记录操作日志的必要 | ||
| 45 | public CommonResult<String> uploadFile(@RequestParam("file") MultipartFile file, | 47 | public CommonResult<String> uploadFile(@RequestParam("file") MultipartFile file, |
| 46 | - @RequestParam("path") String path) throws Exception { | 48 | + @RequestParam(value = "path", required = false) String path) throws Exception { |
| 47 | return success(fileService.createFile(path, IoUtil.readBytes(file.getInputStream()))); | 49 | return success(fileService.createFile(path, IoUtil.readBytes(file.getInputStream()))); |
| 48 | } | 50 | } |
| 49 | 51 |
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java
| @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.infra.service.file; | @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.infra.service.file; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.io.FileTypeUtil; | 3 | import cn.hutool.core.io.FileTypeUtil; |
| 4 | import cn.hutool.core.lang.Assert; | 4 | import cn.hutool.core.lang.Assert; |
| 5 | +import cn.hutool.core.util.StrUtil; | ||
| 6 | +import cn.hutool.crypto.digest.DigestUtil; | ||
| 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 7 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | import cn.iocoder.yudao.framework.file.core.client.FileClient; | 8 | import cn.iocoder.yudao.framework.file.core.client.FileClient; |
| 7 | import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO; | 9 | import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO; |
| @@ -36,6 +38,12 @@ public class FileServiceImpl implements FileService { | @@ -36,6 +38,12 @@ public class FileServiceImpl implements FileService { | ||
| 36 | 38 | ||
| 37 | @Override | 39 | @Override |
| 38 | public String createFile(String path, byte[] content) throws Exception { | 40 | public String createFile(String path, byte[] content) throws Exception { |
| 41 | + // 计算默认的 path 名 | ||
| 42 | + String type = FileTypeUtil.getType(new ByteArrayInputStream(content)); | ||
| 43 | + if (StrUtil.isEmpty(path)) { | ||
| 44 | + path = DigestUtil.md5Hex(content) + '.' + type; | ||
| 45 | + } | ||
| 46 | + | ||
| 39 | // 上传到文件存储器 | 47 | // 上传到文件存储器 |
| 40 | FileClient client = fileConfigService.getMasterFileClient(); | 48 | FileClient client = fileConfigService.getMasterFileClient(); |
| 41 | Assert.notNull(client, "客户端(master) 不能为空"); | 49 | Assert.notNull(client, "客户端(master) 不能为空"); |
| @@ -46,7 +54,7 @@ public class FileServiceImpl implements FileService { | @@ -46,7 +54,7 @@ public class FileServiceImpl implements FileService { | ||
| 46 | file.setConfigId(client.getId()); | 54 | file.setConfigId(client.getId()); |
| 47 | file.setPath(path); | 55 | file.setPath(path); |
| 48 | file.setUrl(url); | 56 | file.setUrl(url); |
| 49 | - file.setType(FileTypeUtil.getType(new ByteArrayInputStream(content))); | 57 | + file.setType(type); |
| 50 | file.setSize(content.length); | 58 | file.setSize(content.length); |
| 51 | fileMapper.insert(file); | 59 | fileMapper.insert(file); |
| 52 | return url; | 60 | return url; |
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java
| @@ -17,7 +17,6 @@ public interface ErrorCodeConstants { | @@ -17,7 +17,6 @@ public interface ErrorCodeConstants { | ||
| 17 | // ========== AUTH 模块 1004003000 ========== | 17 | // ========== AUTH 模块 1004003000 ========== |
| 18 | ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1004003000, "登录失败,账号密码不正确"); | 18 | ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1004003000, "登录失败,账号密码不正确"); |
| 19 | ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1004003001, "登录失败,账号被禁用"); | 19 | ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1004003001, "登录失败,账号被禁用"); |
| 20 | - ErrorCode AUTH_LOGIN_FAIL_UNKNOWN = new ErrorCode(1004003002, "登录失败"); // 登录失败的兜底,未知原因 | ||
| 21 | ErrorCode AUTH_TOKEN_EXPIRED = new ErrorCode(1004003004, "Token 已经过期"); | 20 | ErrorCode AUTH_TOKEN_EXPIRED = new ErrorCode(1004003004, "Token 已经过期"); |
| 22 | ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1004003005, "未绑定账号,需要进行绑定"); | 21 | ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1004003005, "未绑定账号,需要进行绑定"); |
| 23 | 22 |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.http
| 1 | ### 请求 /login 接口 => 成功 | 1 | ### 请求 /login 接口 => 成功 |
| 2 | -POST {{appApi}}/member/login | 2 | +POST {{appApi}}/member/auth/login |
| 3 | Content-Type: application/json | 3 | Content-Type: application/json |
| 4 | tenant-id: {{appTenentId}} | 4 | tenant-id: {{appTenentId}} |
| 5 | 5 | ||
| @@ -19,7 +19,7 @@ tenant-id: {{appTenentId}} | @@ -19,7 +19,7 @@ tenant-id: {{appTenentId}} | ||
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | ### 请求 /sms-login 接口 => 成功 | 21 | ### 请求 /sms-login 接口 => 成功 |
| 22 | -POST {{appApi}}/member/sms-login | 22 | +POST {{appApi}}/member/auth/sms-login |
| 23 | Content-Type: application/json | 23 | Content-Type: application/json |
| 24 | tenant-id: {{appTenentId}} | 24 | tenant-id: {{appTenentId}} |
| 25 | 25 | ||
| @@ -29,7 +29,12 @@ tenant-id: {{appTenentId}} | @@ -29,7 +29,12 @@ tenant-id: {{appTenentId}} | ||
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | ### 请求 /logout 接口 => 成功 | 31 | ### 请求 /logout 接口 => 成功 |
| 32 | -POST {{appApi}}/member/logout | 32 | +POST {{appApi}}/member/auth/logout |
| 33 | Content-Type: application/json | 33 | Content-Type: application/json |
| 34 | Authorization: Bearer c1b76bdaf2c146c581caa4d7fd81ee66 | 34 | Authorization: Bearer c1b76bdaf2c146c581caa4d7fd81ee66 |
| 35 | tenant-id: {{appTenentId}} | 35 | tenant-id: {{appTenentId}} |
| 36 | + | ||
| 37 | +### 请求 /auth/refresh-token 接口 => 成功 | ||
| 38 | +POST {{appApi}}/member/auth/refresh-token?refreshToken=bc43d929094849a28b3a69f6e6940d70 | ||
| 39 | +Content-Type: application/json | ||
| 40 | +tenant-id: {{appTenentId}} |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/AppAuthController.java
| 1 | package cn.iocoder.yudao.module.member.controller.app.auth; | 1 | package cn.iocoder.yudao.module.member.controller.app.auth; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.util.StrUtil; | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; | ||
| 6 | +import cn.iocoder.yudao.framework.security.config.SecurityProperties; | ||
| 4 | import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; | 7 | import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; |
| 8 | +import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; | ||
| 5 | import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; | 9 | import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; |
| 6 | import cn.iocoder.yudao.module.member.service.auth.MemberAuthService; | 10 | import cn.iocoder.yudao.module.member.service.auth.MemberAuthService; |
| 7 | import io.swagger.annotations.Api; | 11 | import io.swagger.annotations.Api; |
| @@ -13,11 +17,10 @@ import org.springframework.validation.annotation.Validated; | @@ -13,11 +17,10 @@ import org.springframework.validation.annotation.Validated; | ||
| 13 | import org.springframework.web.bind.annotation.*; | 17 | import org.springframework.web.bind.annotation.*; |
| 14 | 18 | ||
| 15 | import javax.annotation.Resource; | 19 | import javax.annotation.Resource; |
| 20 | +import javax.servlet.http.HttpServletRequest; | ||
| 16 | import javax.validation.Valid; | 21 | import javax.validation.Valid; |
| 17 | 22 | ||
| 18 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 23 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 19 | -import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; | ||
| 20 | -import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getUserAgent; | ||
| 21 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | 24 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| 22 | 25 | ||
| 23 | @Api(tags = "用户 APP - 认证") | 26 | @Api(tags = "用户 APP - 认证") |
| @@ -30,19 +33,39 @@ public class AppAuthController { | @@ -30,19 +33,39 @@ public class AppAuthController { | ||
| 30 | @Resource | 33 | @Resource |
| 31 | private MemberAuthService authService; | 34 | private MemberAuthService authService; |
| 32 | 35 | ||
| 36 | + @Resource | ||
| 37 | + private SecurityProperties securityProperties; | ||
| 38 | + | ||
| 33 | @PostMapping("/login") | 39 | @PostMapping("/login") |
| 34 | @ApiOperation("使用手机 + 密码登录") | 40 | @ApiOperation("使用手机 + 密码登录") |
| 35 | public CommonResult<AppAuthLoginRespVO> login(@RequestBody @Valid AppAuthLoginReqVO reqVO) { | 41 | public CommonResult<AppAuthLoginRespVO> login(@RequestBody @Valid AppAuthLoginReqVO reqVO) { |
| 36 | - String token = authService.login(reqVO, getClientIP(), getUserAgent()); | ||
| 37 | - return success(AppAuthLoginRespVO.builder().token(token).build()); | 42 | + return success(authService.login(reqVO)); |
| 43 | + } | ||
| 44 | + | ||
| 45 | + @PostMapping("/logout") | ||
| 46 | + @ApiOperation("登出系统") | ||
| 47 | + public CommonResult<Boolean> logout(HttpServletRequest request) { | ||
| 48 | + String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader()); | ||
| 49 | + if (StrUtil.isNotBlank(token)) { | ||
| 50 | + authService.logout(token); | ||
| 51 | + } | ||
| 52 | + return success(true); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + @PostMapping("/refresh-token") | ||
| 56 | + @ApiOperation("刷新令牌") | ||
| 57 | + @ApiImplicitParam(name = "refreshToken", value = "刷新令牌", required = true, dataTypeClass = String.class) | ||
| 58 | + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 | ||
| 59 | + public CommonResult<AppAuthLoginRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken) { | ||
| 60 | + return success(authService.refreshToken(refreshToken)); | ||
| 38 | } | 61 | } |
| 39 | 62 | ||
| 63 | + // ========== 短信登录相关 ========== | ||
| 64 | + | ||
| 40 | @PostMapping("/sms-login") | 65 | @PostMapping("/sms-login") |
| 41 | @ApiOperation("使用手机 + 验证码登录") | 66 | @ApiOperation("使用手机 + 验证码登录") |
| 42 | public CommonResult<AppAuthLoginRespVO> smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO) { | 67 | public CommonResult<AppAuthLoginRespVO> smsLogin(@RequestBody @Valid AppAuthSmsLoginReqVO reqVO) { |
| 43 | - String token = authService.smsLogin(reqVO, getClientIP(), getUserAgent()); | ||
| 44 | - // 返回结果 | ||
| 45 | - return success(AppAuthLoginRespVO.builder().token(token).build()); | 68 | + return success(authService.smsLogin(reqVO)); |
| 46 | } | 69 | } |
| 47 | 70 | ||
| 48 | @PostMapping("/send-sms-code") | 71 | @PostMapping("/send-sms-code") |
| @@ -83,16 +106,14 @@ public class AppAuthController { | @@ -83,16 +106,14 @@ public class AppAuthController { | ||
| 83 | 106 | ||
| 84 | @PostMapping("/social-quick-login") | 107 | @PostMapping("/social-quick-login") |
| 85 | @ApiOperation(value = "社交快捷登录,使用 code 授权码", notes = "适合未登录的用户,但是社交账号已绑定用户") | 108 | @ApiOperation(value = "社交快捷登录,使用 code 授权码", notes = "适合未登录的用户,但是社交账号已绑定用户") |
| 86 | - public CommonResult<AppAuthLoginRespVO> socialLogin(@RequestBody @Valid AppAuthSocialQuickLoginReqVO reqVO) { | ||
| 87 | - String token = authService.socialQuickLogin(reqVO, getClientIP(), getUserAgent()); | ||
| 88 | - return success(AppAuthLoginRespVO.builder().token(token).build()); | 109 | + public CommonResult<AppAuthLoginRespVO> socialQuickLogin(@RequestBody @Valid AppAuthSocialQuickLoginReqVO reqVO) { |
| 110 | + return success(authService.socialQuickLogin(reqVO)); | ||
| 89 | } | 111 | } |
| 90 | 112 | ||
| 91 | @PostMapping("/social-bind-login") | 113 | @PostMapping("/social-bind-login") |
| 92 | @ApiOperation(value = "社交绑定登录,使用 手机号 + 手机验证码", notes = "适合未登录的用户,进行登录 + 绑定") | 114 | @ApiOperation(value = "社交绑定登录,使用 手机号 + 手机验证码", notes = "适合未登录的用户,进行登录 + 绑定") |
| 93 | - public CommonResult<AppAuthLoginRespVO> socialLogin2(@RequestBody @Valid AppAuthSocialBindLoginReqVO reqVO) { | ||
| 94 | - String token = authService.socialBindLogin(reqVO, getClientIP(), getUserAgent()); | ||
| 95 | - return success(AppAuthLoginRespVO.builder().token(token).build()); | 115 | + public CommonResult<AppAuthLoginRespVO> socialBindLogin(@RequestBody @Valid AppAuthSocialBindLoginReqVO reqVO) { |
| 116 | + return success(authService.socialBindLogin(reqVO)); | ||
| 96 | } | 117 | } |
| 97 | 118 | ||
| 98 | } | 119 | } |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/auth/vo/AppAuthLoginRespVO.java
| @@ -7,14 +7,25 @@ import lombok.Builder; | @@ -7,14 +7,25 @@ import lombok.Builder; | ||
| 7 | import lombok.Data; | 7 | import lombok.Data; |
| 8 | import lombok.NoArgsConstructor; | 8 | import lombok.NoArgsConstructor; |
| 9 | 9 | ||
| 10 | -@ApiModel("用户 APP - 手机密码登录 Response VO") | 10 | +import java.util.Date; |
| 11 | + | ||
| 12 | +@ApiModel("用户 APP - 登录 Response VO") | ||
| 11 | @Data | 13 | @Data |
| 12 | @NoArgsConstructor | 14 | @NoArgsConstructor |
| 13 | @AllArgsConstructor | 15 | @AllArgsConstructor |
| 14 | @Builder | 16 | @Builder |
| 15 | public class AppAuthLoginRespVO { | 17 | public class AppAuthLoginRespVO { |
| 16 | 18 | ||
| 17 | - @ApiModelProperty(value = "token", required = true, example = "yudaoyuanma") | ||
| 18 | - private String token; | 19 | + @ApiModelProperty(value = "用户编号", required = true, example = "1024") |
| 20 | + private Long userId; | ||
| 21 | + | ||
| 22 | + @ApiModelProperty(value = "访问令牌", required = true, example = "happy") | ||
| 23 | + private String accessToken; | ||
| 24 | + | ||
| 25 | + @ApiModelProperty(value = "刷新令牌", required = true, example = "nice") | ||
| 26 | + private String refreshToken; | ||
| 27 | + | ||
| 28 | + @ApiModelProperty(value = "过期时间", required = true) | ||
| 29 | + private Date expiresTime; | ||
| 19 | 30 | ||
| 20 | } | 31 | } |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/auth/AuthConvert.java
| 1 | package cn.iocoder.yudao.module.member.convert.auth; | 1 | package cn.iocoder.yudao.module.member.convert.auth; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||
| 4 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 5 | import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; | 3 | import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; |
| 6 | import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO; | 4 | import cn.iocoder.yudao.module.member.controller.app.social.vo.AppSocialUserUnbindReqVO; |
| 7 | -import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; | 5 | +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO; |
| 8 | import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; | 6 | import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; |
| 9 | import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; | 7 | import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; |
| 10 | import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; | 8 | import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; |
| 11 | import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO; | 9 | import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO; |
| 12 | import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; | 10 | import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; |
| 13 | import org.mapstruct.Mapper; | 11 | import org.mapstruct.Mapper; |
| 14 | -import org.mapstruct.Mapping; | ||
| 15 | import org.mapstruct.factory.Mappers; | 12 | import org.mapstruct.factory.Mappers; |
| 16 | 13 | ||
| 17 | @Mapper | 14 | @Mapper |
| @@ -19,14 +16,6 @@ public interface AuthConvert { | @@ -19,14 +16,6 @@ public interface AuthConvert { | ||
| 19 | 16 | ||
| 20 | AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class); | 17 | AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class); |
| 21 | 18 | ||
| 22 | - @Mapping(source = "mobile", target = "username") | ||
| 23 | - LoginUser convert0(MemberUserDO bean); | ||
| 24 | - | ||
| 25 | - default LoginUser convert(MemberUserDO bean) { | ||
| 26 | - // 目的,为了设置 UserTypeEnum.MEMBER.getValue() | ||
| 27 | - return convert0(bean).setUserType(UserTypeEnum.MEMBER.getValue()); | ||
| 28 | - } | ||
| 29 | - | ||
| 30 | SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialBindLoginReqVO reqVO); | 19 | SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialBindLoginReqVO reqVO); |
| 31 | SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialQuickLoginReqVO reqVO); | 20 | SocialUserBindReqDTO convert(Long userId, Integer userType, AppAuthSocialQuickLoginReqVO reqVO); |
| 32 | SocialUserUnbindReqDTO convert(Long userId, Integer userType, AppSocialUserUnbindReqVO reqVO); | 21 | SocialUserUnbindReqDTO convert(Long userId, Integer userType, AppSocialUserUnbindReqVO reqVO); |
| @@ -35,4 +24,6 @@ public interface AuthConvert { | @@ -35,4 +24,6 @@ public interface AuthConvert { | ||
| 35 | SmsCodeUseReqDTO convert(AppAuthResetPasswordReqVO reqVO, SmsSceneEnum scene, String usedIp); | 24 | SmsCodeUseReqDTO convert(AppAuthResetPasswordReqVO reqVO, SmsSceneEnum scene, String usedIp); |
| 36 | SmsCodeUseReqDTO convert(AppAuthSmsLoginReqVO reqVO, Integer scene, String usedIp); | 25 | SmsCodeUseReqDTO convert(AppAuthSmsLoginReqVO reqVO, Integer scene, String usedIp); |
| 37 | 26 | ||
| 27 | + AppAuthLoginRespVO convert(OAuth2AccessTokenRespDTO bean); | ||
| 28 | + | ||
| 38 | } | 29 | } |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/framework/package-info.java
0 → 100644
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/framework/security/config/SecurityConfiguration.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.member.framework.security.config; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer; | ||
| 4 | +import org.springframework.context.annotation.Bean; | ||
| 5 | +import org.springframework.context.annotation.Configuration; | ||
| 6 | +import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||
| 7 | +import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * Member 模块的 Security 配置 | ||
| 11 | + */ | ||
| 12 | +@Configuration("memberSecurityConfiguration") | ||
| 13 | +public class SecurityConfiguration { | ||
| 14 | + | ||
| 15 | + @Bean("memberAuthorizeRequestsCustomizer") | ||
| 16 | + public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { | ||
| 17 | + return new AuthorizeRequestsCustomizer() { | ||
| 18 | + | ||
| 19 | + @Override | ||
| 20 | + public void customize(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) { | ||
| 21 | + // 登录的接口 | ||
| 22 | + registry.antMatchers(buildAdminApi("/member/auth/logout")).permitAll(); | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + }; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | +} |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/framework/security/core/package-info.java
0 → 100644
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthService.java
| 1 | package cn.iocoder.yudao.module.member.service.auth; | 1 | package cn.iocoder.yudao.module.member.service.auth; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService; | ||
| 4 | import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; | 3 | import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; |
| 5 | 4 | ||
| 6 | import javax.validation.Valid; | 5 | import javax.validation.Valid; |
| @@ -12,47 +11,46 @@ import javax.validation.Valid; | @@ -12,47 +11,46 @@ import javax.validation.Valid; | ||
| 12 | * | 11 | * |
| 13 | * @author 芋道源码 | 12 | * @author 芋道源码 |
| 14 | */ | 13 | */ |
| 15 | -public interface MemberAuthService extends SecurityAuthFrameworkService { | 14 | +public interface MemberAuthService { |
| 16 | 15 | ||
| 17 | /** | 16 | /** |
| 18 | * 手机 + 密码登录 | 17 | * 手机 + 密码登录 |
| 19 | * | 18 | * |
| 20 | * @param reqVO 登录信息 | 19 | * @param reqVO 登录信息 |
| 21 | - * @param userIp 用户 IP | ||
| 22 | - * @param userAgent 用户 UA | ||
| 23 | - * @return 身份令牌,使用 JWT 方式 | 20 | + * @return 登录结果 |
| 24 | */ | 21 | */ |
| 25 | - String login(@Valid AppAuthLoginReqVO reqVO, String userIp, String userAgent); | 22 | + AppAuthLoginRespVO login(@Valid AppAuthLoginReqVO reqVO); |
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 基于 token 退出登录 | ||
| 26 | + * | ||
| 27 | + * @param token token | ||
| 28 | + */ | ||
| 29 | + void logout(String token); | ||
| 26 | 30 | ||
| 27 | /** | 31 | /** |
| 28 | * 手机 + 验证码登陆 | 32 | * 手机 + 验证码登陆 |
| 29 | * | 33 | * |
| 30 | * @param reqVO 登陆信息 | 34 | * @param reqVO 登陆信息 |
| 31 | - * @param userIp 用户 IP | ||
| 32 | - * @param userAgent 用户 UA | ||
| 33 | - * @return 身份令牌,使用 JWT 方式 | 35 | + * @return 登录结果 |
| 34 | */ | 36 | */ |
| 35 | - String smsLogin(@Valid AppAuthSmsLoginReqVO reqVO, String userIp, String userAgent); | 37 | + AppAuthLoginRespVO smsLogin(@Valid AppAuthSmsLoginReqVO reqVO); |
| 36 | 38 | ||
| 37 | /** | 39 | /** |
| 38 | * 社交登录,使用 code 授权码 | 40 | * 社交登录,使用 code 授权码 |
| 39 | * | 41 | * |
| 40 | * @param reqVO 登录信息 | 42 | * @param reqVO 登录信息 |
| 41 | - * @param userIp 用户 IP | ||
| 42 | - * @param userAgent 用户 UA | ||
| 43 | - * @return 身份令牌,使用 JWT 方式 | 43 | + * @return 登录结果 |
| 44 | */ | 44 | */ |
| 45 | - String socialQuickLogin(@Valid AppAuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent); | 45 | + AppAuthLoginRespVO socialQuickLogin(@Valid AppAuthSocialQuickLoginReqVO reqVO); |
| 46 | 46 | ||
| 47 | /** | 47 | /** |
| 48 | * 社交登录,使用 手机号 + 手机验证码 | 48 | * 社交登录,使用 手机号 + 手机验证码 |
| 49 | * | 49 | * |
| 50 | * @param reqVO 登录信息 | 50 | * @param reqVO 登录信息 |
| 51 | - * @param userIp 用户 IP | ||
| 52 | - * @param userAgent 用户 UA | ||
| 53 | - * @return 身份令牌,使用 JWT 方式 | 51 | + * @return 登录结果 |
| 54 | */ | 52 | */ |
| 55 | - String socialBindLogin(@Valid AppAuthSocialBindLoginReqVO reqVO, String userIp, String userAgent); | 53 | + AppAuthLoginRespVO socialBindLogin(@Valid AppAuthSocialBindLoginReqVO reqVO); |
| 56 | 54 | ||
| 57 | /** | 55 | /** |
| 58 | * 获得社交认证 URL | 56 | * 获得社交认证 URL |
| @@ -84,4 +82,11 @@ public interface MemberAuthService extends SecurityAuthFrameworkService { | @@ -84,4 +82,11 @@ public interface MemberAuthService extends SecurityAuthFrameworkService { | ||
| 84 | */ | 82 | */ |
| 85 | void sendSmsCode(Long userId, AppAuthSmsSendReqVO reqVO); | 83 | void sendSmsCode(Long userId, AppAuthSmsSendReqVO reqVO); |
| 86 | 84 | ||
| 85 | + /** | ||
| 86 | + * 刷新访问令牌 | ||
| 87 | + * | ||
| 88 | + * @param refreshToken 刷新令牌 | ||
| 89 | + * @return 登录结果 | ||
| 90 | + */ | ||
| 91 | + AppAuthLoginRespVO refreshToken(String refreshToken); | ||
| 87 | } | 92 | } |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java
| 1 | package cn.iocoder.yudao.module.member.service.auth; | 1 | package cn.iocoder.yudao.module.member.service.auth; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.lang.Assert; | 3 | import cn.hutool.core.lang.Assert; |
| 4 | +import cn.hutool.core.util.ObjectUtil; | ||
| 4 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 5 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 5 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | 6 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 6 | import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; | 7 | import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; |
| 7 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; | 8 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; |
| 8 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 9 | -import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken; | ||
| 10 | import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; | 9 | import cn.iocoder.yudao.module.member.controller.app.auth.vo.*; |
| 11 | import cn.iocoder.yudao.module.member.convert.auth.AuthConvert; | 10 | import cn.iocoder.yudao.module.member.convert.auth.AuthConvert; |
| 12 | import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; | 11 | import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; |
| 13 | import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; | 12 | import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; |
| 14 | import cn.iocoder.yudao.module.member.service.user.MemberUserService; | 13 | import cn.iocoder.yudao.module.member.service.user.MemberUserService; |
| 15 | -import cn.iocoder.yudao.module.system.api.auth.UserSessionApi; | 14 | +import cn.iocoder.yudao.module.system.api.auth.OAuth2TokenApi; |
| 15 | +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCreateReqDTO; | ||
| 16 | +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO; | ||
| 16 | import cn.iocoder.yudao.module.system.api.logger.LoginLogApi; | 17 | import cn.iocoder.yudao.module.system.api.logger.LoginLogApi; |
| 17 | import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; | 18 | import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; |
| 18 | import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; | 19 | import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; |
| 19 | import cn.iocoder.yudao.module.system.api.social.SocialUserApi; | 20 | import cn.iocoder.yudao.module.system.api.social.SocialUserApi; |
| 21 | +import cn.iocoder.yudao.module.system.enums.auth.OAuth2ClientIdEnum; | ||
| 20 | import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; | 22 | import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; |
| 21 | import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; | 23 | import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; |
| 22 | import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; | 24 | import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; |
| 23 | import com.google.common.annotations.VisibleForTesting; | 25 | import com.google.common.annotations.VisibleForTesting; |
| 24 | import lombok.extern.slf4j.Slf4j; | 26 | import lombok.extern.slf4j.Slf4j; |
| 25 | -import org.springframework.context.annotation.Lazy; | ||
| 26 | -import org.springframework.security.authentication.AuthenticationManager; | ||
| 27 | -import org.springframework.security.authentication.BadCredentialsException; | ||
| 28 | -import org.springframework.security.authentication.DisabledException; | ||
| 29 | -import org.springframework.security.core.Authentication; | ||
| 30 | -import org.springframework.security.core.AuthenticationException; | ||
| 31 | -import org.springframework.security.core.userdetails.UserDetails; | ||
| 32 | -import org.springframework.security.core.userdetails.UsernameNotFoundException; | ||
| 33 | import org.springframework.security.crypto.password.PasswordEncoder; | 27 | import org.springframework.security.crypto.password.PasswordEncoder; |
| 34 | import org.springframework.stereotype.Service; | 28 | import org.springframework.stereotype.Service; |
| 35 | import org.springframework.transaction.annotation.Transactional; | 29 | import org.springframework.transaction.annotation.Transactional; |
| @@ -51,19 +45,15 @@ import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*; | @@ -51,19 +45,15 @@ import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*; | ||
| 51 | public class MemberAuthServiceImpl implements MemberAuthService { | 45 | public class MemberAuthServiceImpl implements MemberAuthService { |
| 52 | 46 | ||
| 53 | @Resource | 47 | @Resource |
| 54 | - @Lazy // 延迟加载,因为存在相互依赖的问题 | ||
| 55 | - private AuthenticationManager authenticationManager; | ||
| 56 | - | ||
| 57 | - @Resource | ||
| 58 | private MemberUserService userService; | 48 | private MemberUserService userService; |
| 59 | @Resource | 49 | @Resource |
| 60 | private SmsCodeApi smsCodeApi; | 50 | private SmsCodeApi smsCodeApi; |
| 61 | @Resource | 51 | @Resource |
| 62 | private LoginLogApi loginLogApi; | 52 | private LoginLogApi loginLogApi; |
| 63 | @Resource | 53 | @Resource |
| 64 | - private UserSessionApi userSessionApi; | ||
| 65 | - @Resource | ||
| 66 | private SocialUserApi socialUserApi; | 54 | private SocialUserApi socialUserApi; |
| 55 | + @Resource | ||
| 56 | + private OAuth2TokenApi oauth2TokenApi; | ||
| 67 | 57 | ||
| 68 | @Resource | 58 | @Resource |
| 69 | private PasswordEncoder passwordEncoder; | 59 | private PasswordEncoder passwordEncoder; |
| @@ -71,44 +61,31 @@ public class MemberAuthServiceImpl implements MemberAuthService { | @@ -71,44 +61,31 @@ public class MemberAuthServiceImpl implements MemberAuthService { | ||
| 71 | private MemberUserMapper userMapper; | 61 | private MemberUserMapper userMapper; |
| 72 | 62 | ||
| 73 | @Override | 63 | @Override |
| 74 | - public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException { | ||
| 75 | - // 获取 username 对应的 SysUserDO | ||
| 76 | - MemberUserDO user = userService.getUserByMobile(mobile); | ||
| 77 | - if (user == null) { | ||
| 78 | - throw new UsernameNotFoundException(mobile); | ||
| 79 | - } | ||
| 80 | - // 创建 LoginUser 对象 | ||
| 81 | - return AuthConvert.INSTANCE.convert(user); | ||
| 82 | - } | ||
| 83 | - | ||
| 84 | - @Override | ||
| 85 | - public String login(AppAuthLoginReqVO reqVO, String userIp, String userAgent) { | 64 | + public AppAuthLoginRespVO login(AppAuthLoginReqVO reqVO) { |
| 86 | // 使用手机 + 密码,进行登录。 | 65 | // 使用手机 + 密码,进行登录。 |
| 87 | - LoginUser loginUser = this.login0(reqVO.getMobile(), reqVO.getPassword()); | 66 | + MemberUserDO user = login0(reqVO.getMobile(), reqVO.getPassword()); |
| 88 | 67 | ||
| 89 | - // 缓存登录用户到 Redis 中,返回 Token 令牌 | ||
| 90 | - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); | 68 | + // 创建 Token 令牌,记录登录日志 |
| 69 | + return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE); | ||
| 91 | } | 70 | } |
| 92 | 71 | ||
| 93 | @Override | 72 | @Override |
| 94 | @Transactional | 73 | @Transactional |
| 95 | - public String smsLogin(AppAuthSmsLoginReqVO reqVO, String userIp, String userAgent) { | 74 | + public AppAuthLoginRespVO smsLogin(AppAuthSmsLoginReqVO reqVO) { |
| 96 | // 校验验证码 | 75 | // 校验验证码 |
| 76 | + String userIp = getClientIP(); | ||
| 97 | smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_LOGIN.getScene(), userIp)); | 77 | smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MEMBER_LOGIN.getScene(), userIp)); |
| 98 | 78 | ||
| 99 | // 获得获得注册用户 | 79 | // 获得获得注册用户 |
| 100 | MemberUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp); | 80 | MemberUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp); |
| 101 | Assert.notNull(user, "获取用户失败,结果为空"); | 81 | Assert.notNull(user, "获取用户失败,结果为空"); |
| 102 | 82 | ||
| 103 | - // 执行登陆 | ||
| 104 | - LoginUser loginUser = AuthConvert.INSTANCE.convert(user); | ||
| 105 | - | ||
| 106 | - // 缓存登录用户到 Redis 中,返回 Token 令牌 | ||
| 107 | - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SMS, userIp, userAgent); | 83 | + // 创建 Token 令牌,记录登录日志 |
| 84 | + return createTokenAfterLoginSuccess(user, reqVO.getMobile(), LoginLogTypeEnum.LOGIN_SMS); | ||
| 108 | } | 85 | } |
| 109 | 86 | ||
| 110 | @Override | 87 | @Override |
| 111 | - public String socialQuickLogin(AppAuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent) { | 88 | + public AppAuthLoginRespVO socialQuickLogin(AppAuthSocialQuickLoginReqVO reqVO) { |
| 112 | // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 | 89 | // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 |
| 113 | Long userId = socialUserApi.getBindUserId(UserTypeEnum.MEMBER.getValue(), reqVO.getType(), | 90 | Long userId = socialUserApi.getBindUserId(UserTypeEnum.MEMBER.getValue(), reqVO.getType(), |
| 114 | reqVO.getCode(), reqVO.getState()); | 91 | reqVO.getCode(), reqVO.getState()); |
| @@ -122,31 +99,30 @@ public class MemberAuthServiceImpl implements MemberAuthService { | @@ -122,31 +99,30 @@ public class MemberAuthServiceImpl implements MemberAuthService { | ||
| 122 | throw exception(USER_NOT_EXISTS); | 99 | throw exception(USER_NOT_EXISTS); |
| 123 | } | 100 | } |
| 124 | 101 | ||
| 125 | - // 创建 LoginUser 对象 | ||
| 126 | - LoginUser loginUser = AuthConvert.INSTANCE.convert(user); | ||
| 127 | - | ||
| 128 | - // 缓存登录用户到 Redis 中,返回 Token 令牌 | ||
| 129 | - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); | 102 | + // 创建 Token 令牌,记录登录日志 |
| 103 | + return createTokenAfterLoginSuccess(user, null, LoginLogTypeEnum.LOGIN_SOCIAL); | ||
| 130 | } | 104 | } |
| 131 | 105 | ||
| 132 | @Override | 106 | @Override |
| 133 | - public String socialBindLogin(AppAuthSocialBindLoginReqVO reqVO, String userIp, String userAgent) { | 107 | + public AppAuthLoginRespVO socialBindLogin(AppAuthSocialBindLoginReqVO reqVO) { |
| 134 | // 使用手机号、手机验证码登录 | 108 | // 使用手机号、手机验证码登录 |
| 135 | AppAuthSmsLoginReqVO loginReqVO = AppAuthSmsLoginReqVO.builder() | 109 | AppAuthSmsLoginReqVO loginReqVO = AppAuthSmsLoginReqVO.builder() |
| 136 | .mobile(reqVO.getMobile()).code(reqVO.getSmsCode()).build(); | 110 | .mobile(reqVO.getMobile()).code(reqVO.getSmsCode()).build(); |
| 137 | - String token = this.smsLogin(loginReqVO, userIp, userAgent); | ||
| 138 | - LoginUser loginUser = userSessionApi.getLoginUser(token); | 111 | + AppAuthLoginRespVO token = smsLogin(loginReqVO); |
| 139 | 112 | ||
| 140 | // 绑定社交用户 | 113 | // 绑定社交用户 |
| 141 | - socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO)); | 114 | + socialUserApi.bindSocialUser(AuthConvert.INSTANCE.convert(token.getUserId(), getUserType().getValue(), reqVO)); |
| 142 | return token; | 115 | return token; |
| 143 | } | 116 | } |
| 144 | 117 | ||
| 145 | - private String createUserSessionAfterLoginSuccess(LoginUser loginUser, LoginLogTypeEnum logType, String userIp, String userAgent) { | 118 | + private AppAuthLoginRespVO createTokenAfterLoginSuccess(MemberUserDO user, String mobile, LoginLogTypeEnum logType) { |
| 146 | // 插入登陆日志 | 119 | // 插入登陆日志 |
| 147 | - createLoginLog(loginUser.getUsername(), logType, LoginResultEnum.SUCCESS); | ||
| 148 | - // 缓存登录用户到 Redis 中,返回 Token 令牌 | ||
| 149 | - return userSessionApi.createUserSession(loginUser, userIp, userAgent); | 120 | + createLoginLog(user.getId(), mobile, logType, LoginResultEnum.SUCCESS); |
| 121 | + // 创建 Token 令牌 | ||
| 122 | + OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.createAccessToken(new OAuth2AccessTokenCreateReqDTO() | ||
| 123 | + .setUserId(user.getId()).setUserType(getUserType().getValue()).setClientId(OAuth2ClientIdEnum.DEFAULT.getId())); | ||
| 124 | + // 构建返回结果 | ||
| 125 | + return AuthConvert.INSTANCE.convert(accessTokenRespDTO); | ||
| 150 | } | 126 | } |
| 151 | 127 | ||
| 152 | @Override | 128 | @Override |
| @@ -154,40 +130,32 @@ public class MemberAuthServiceImpl implements MemberAuthService { | @@ -154,40 +130,32 @@ public class MemberAuthServiceImpl implements MemberAuthService { | ||
| 154 | return socialUserApi.getAuthorizeUrl(type, redirectUri); | 130 | return socialUserApi.getAuthorizeUrl(type, redirectUri); |
| 155 | } | 131 | } |
| 156 | 132 | ||
| 157 | - private LoginUser login0(String username, String password) { | ||
| 158 | - final LoginLogTypeEnum logType = LoginLogTypeEnum.LOGIN_USERNAME; | ||
| 159 | - // 用户验证 | ||
| 160 | - Authentication authentication; | ||
| 161 | - try { | ||
| 162 | - // 调用 Spring Security 的 AuthenticationManager#authenticate(...) 方法,使用账号密码进行认证 | ||
| 163 | - // 在其内部,会调用到 loadUserByUsername 方法,获取 User 信息 | ||
| 164 | - authentication = authenticationManager.authenticate(new MultiUsernamePasswordAuthenticationToken( | ||
| 165 | - username, password, getUserType())); | ||
| 166 | - } catch (BadCredentialsException badCredentialsException) { | ||
| 167 | - this.createLoginLog(username, logType, LoginResultEnum.BAD_CREDENTIALS); | 133 | + private MemberUserDO login0(String mobile, String password) { |
| 134 | + final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_MOBILE; | ||
| 135 | + // 校验账号是否存在 | ||
| 136 | + MemberUserDO user = userService.getUserByMobile(mobile); | ||
| 137 | + if (user == null) { | ||
| 138 | + createLoginLog(null, mobile, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); | ||
| 168 | throw exception(AUTH_LOGIN_BAD_CREDENTIALS); | 139 | throw exception(AUTH_LOGIN_BAD_CREDENTIALS); |
| 169 | - } catch (DisabledException disabledException) { | ||
| 170 | - this.createLoginLog(username, logType, LoginResultEnum.USER_DISABLED); | 140 | + } |
| 141 | + if (!userService.isPasswordMatch(password, user.getPassword())) { | ||
| 142 | + createLoginLog(user.getId(), mobile, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); | ||
| 143 | + throw exception(AUTH_LOGIN_BAD_CREDENTIALS); | ||
| 144 | + } | ||
| 145 | + // 校验是否禁用 | ||
| 146 | + if (ObjectUtil.notEqual(user.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { | ||
| 147 | + createLoginLog(user.getId(), mobile, logTypeEnum, LoginResultEnum.USER_DISABLED); | ||
| 171 | throw exception(AUTH_LOGIN_USER_DISABLED); | 148 | throw exception(AUTH_LOGIN_USER_DISABLED); |
| 172 | - } catch (AuthenticationException authenticationException) { | ||
| 173 | - log.error("[login0][username({}) 发生未知异常]", username, authenticationException); | ||
| 174 | - this.createLoginLog(username, logType, LoginResultEnum.UNKNOWN_ERROR); | ||
| 175 | - throw exception(AUTH_LOGIN_FAIL_UNKNOWN); | ||
| 176 | } | 149 | } |
| 177 | - Assert.notNull(authentication.getPrincipal(), "Principal 不会为空"); | ||
| 178 | - return (LoginUser) authentication.getPrincipal(); | 150 | + return user; |
| 179 | } | 151 | } |
| 180 | 152 | ||
| 181 | - private void createLoginLog(String mobile, LoginLogTypeEnum logType, LoginResultEnum loginResult) { | ||
| 182 | - // 获得用户 | ||
| 183 | - MemberUserDO user = userService.getUserByMobile(mobile); | 153 | + private void createLoginLog(Long userId, String mobile, LoginLogTypeEnum logType, LoginResultEnum loginResult) { |
| 184 | // 插入登录日志 | 154 | // 插入登录日志 |
| 185 | LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); | 155 | LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); |
| 186 | reqDTO.setLogType(logType.getType()); | 156 | reqDTO.setLogType(logType.getType()); |
| 187 | reqDTO.setTraceId(TracerUtils.getTraceId()); | 157 | reqDTO.setTraceId(TracerUtils.getTraceId()); |
| 188 | - if (user != null) { | ||
| 189 | - reqDTO.setUserId(user.getId()); | ||
| 190 | - } | 158 | + reqDTO.setUserId(userId); |
| 191 | reqDTO.setUserType(getUserType().getValue()); | 159 | reqDTO.setUserType(getUserType().getValue()); |
| 192 | reqDTO.setUsername(mobile); | 160 | reqDTO.setUsername(mobile); |
| 193 | reqDTO.setUserAgent(ServletUtils.getUserAgent()); | 161 | reqDTO.setUserAgent(ServletUtils.getUserAgent()); |
| @@ -195,72 +163,20 @@ public class MemberAuthServiceImpl implements MemberAuthService { | @@ -195,72 +163,20 @@ public class MemberAuthServiceImpl implements MemberAuthService { | ||
| 195 | reqDTO.setResult(loginResult.getResult()); | 163 | reqDTO.setResult(loginResult.getResult()); |
| 196 | loginLogApi.createLoginLog(reqDTO); | 164 | loginLogApi.createLoginLog(reqDTO); |
| 197 | // 更新最后登录时间 | 165 | // 更新最后登录时间 |
| 198 | - if (user != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { | ||
| 199 | - userService.updateUserLogin(user.getId(), getClientIP()); | 166 | + if (userId != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { |
| 167 | + userService.updateUserLogin(userId, getClientIP()); | ||
| 200 | } | 168 | } |
| 201 | } | 169 | } |
| 202 | 170 | ||
| 203 | @Override | 171 | @Override |
| 204 | - public LoginUser verifyTokenAndRefresh(String token) { | ||
| 205 | - // 获得 LoginUser | ||
| 206 | - LoginUser loginUser = userSessionApi.getLoginUser(token); | ||
| 207 | - if (loginUser == null) { | ||
| 208 | - return null; | ||
| 209 | - } | ||
| 210 | - // 刷新 LoginUser 缓存 | ||
| 211 | - this.refreshLoginUserCache(token, loginUser); | ||
| 212 | - return loginUser; | ||
| 213 | - } | ||
| 214 | - | ||
| 215 | - private void refreshLoginUserCache(String token, LoginUser loginUser) { | ||
| 216 | - // 每 1/3 的 Session 超时时间,刷新 LoginUser 缓存 | ||
| 217 | - if (System.currentTimeMillis() - loginUser.getUpdateTime().getTime() < | ||
| 218 | - userSessionApi.getSessionTimeoutMillis() / 3) { | ||
| 219 | - return; | ||
| 220 | - } | ||
| 221 | - | ||
| 222 | - // 重新加载 UserDO 信息 | ||
| 223 | - MemberUserDO user = userService.getUser(loginUser.getId()); | ||
| 224 | - if (user == null || CommonStatusEnum.DISABLE.getStatus().equals(user.getStatus())) { | ||
| 225 | - // 校验 token 时,用户被禁用的情况下,也认为 token 过期,方便前端跳转到登录界面 | ||
| 226 | - throw exception(AUTH_TOKEN_EXPIRED); | ||
| 227 | - } | ||
| 228 | - | ||
| 229 | - // 刷新 LoginUser 缓存 | ||
| 230 | - userSessionApi.refreshUserSession(token, loginUser); | ||
| 231 | - } | ||
| 232 | - | ||
| 233 | - @Override | ||
| 234 | - public LoginUser mockLogin(Long userId) { | ||
| 235 | - // 获取用户编号对应的 UserDO | ||
| 236 | - MemberUserDO user = userService.getUser(userId); | ||
| 237 | - if (user == null) { | ||
| 238 | - throw new UsernameNotFoundException(String.valueOf(userId)); | ||
| 239 | - } | ||
| 240 | - | ||
| 241 | - // 执行登陆 | ||
| 242 | - this.createLoginLog(user.getMobile(), LoginLogTypeEnum.LOGIN_MOCK, LoginResultEnum.SUCCESS); | ||
| 243 | - | ||
| 244 | - // 创建 LoginUser 对象 | ||
| 245 | - return AuthConvert.INSTANCE.convert(user); | ||
| 246 | - } | ||
| 247 | - | ||
| 248 | - @Override | ||
| 249 | public void logout(String token) { | 172 | public void logout(String token) { |
| 250 | - // 查询用户信息 | ||
| 251 | - LoginUser loginUser = userSessionApi.getLoginUser(token); | ||
| 252 | - if (loginUser == null) { | 173 | + // 删除访问令牌 |
| 174 | + OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.removeAccessToken(token); | ||
| 175 | + if (accessTokenRespDTO == null) { | ||
| 253 | return; | 176 | return; |
| 254 | } | 177 | } |
| 255 | - // 删除 session | ||
| 256 | - userSessionApi.deleteUserSession(token); | ||
| 257 | - // 记录登出日志 | ||
| 258 | - this.createLogoutLog(loginUser.getId(), loginUser.getUsername()); | ||
| 259 | - } | ||
| 260 | - | ||
| 261 | - @Override | ||
| 262 | - public UserTypeEnum getUserType() { | ||
| 263 | - return UserTypeEnum.MEMBER; | 178 | + // 删除成功,则记录登出日志 |
| 179 | + createLogoutLog(accessTokenRespDTO.getUserId()); | ||
| 264 | } | 180 | } |
| 265 | 181 | ||
| 266 | @Override | 182 | @Override |
| @@ -269,6 +185,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { | @@ -269,6 +185,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { | ||
| 269 | MemberUserDO userDO = checkOldPassword(userId, reqVO.getOldPassword()); | 185 | MemberUserDO userDO = checkOldPassword(userId, reqVO.getOldPassword()); |
| 270 | 186 | ||
| 271 | // 更新用户密码 | 187 | // 更新用户密码 |
| 188 | + // TODO 芋艿:需要重构到用户模块 | ||
| 272 | userMapper.updateById(MemberUserDO.builder().id(userDO.getId()) | 189 | userMapper.updateById(MemberUserDO.builder().id(userDO.getId()) |
| 273 | .password(passwordEncoder.encode(reqVO.getPassword())).build()); | 190 | .password(passwordEncoder.encode(reqVO.getPassword())).build()); |
| 274 | } | 191 | } |
| @@ -293,6 +210,12 @@ public class MemberAuthServiceImpl implements MemberAuthService { | @@ -293,6 +210,12 @@ public class MemberAuthServiceImpl implements MemberAuthService { | ||
| 293 | smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP())); | 210 | smsCodeApi.sendSmsCode(AuthConvert.INSTANCE.convert(reqVO).setCreateIp(getClientIP())); |
| 294 | } | 211 | } |
| 295 | 212 | ||
| 213 | + @Override | ||
| 214 | + public AppAuthLoginRespVO refreshToken(String refreshToken) { | ||
| 215 | + OAuth2AccessTokenRespDTO accessTokenDO = oauth2TokenApi.refreshAccessToken(refreshToken, OAuth2ClientIdEnum.DEFAULT.getId()); | ||
| 216 | + return AuthConvert.INSTANCE.convert(accessTokenDO); | ||
| 217 | + } | ||
| 218 | + | ||
| 296 | /** | 219 | /** |
| 297 | * 校验旧密码 | 220 | * 校验旧密码 |
| 298 | * | 221 | * |
| @@ -321,17 +244,29 @@ public class MemberAuthServiceImpl implements MemberAuthService { | @@ -321,17 +244,29 @@ public class MemberAuthServiceImpl implements MemberAuthService { | ||
| 321 | return user; | 244 | return user; |
| 322 | } | 245 | } |
| 323 | 246 | ||
| 324 | - private void createLogoutLog(Long userId, String username) { | 247 | + private void createLogoutLog(Long userId) { |
| 325 | LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); | 248 | LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); |
| 326 | reqDTO.setLogType(LoginLogTypeEnum.LOGOUT_SELF.getType()); | 249 | reqDTO.setLogType(LoginLogTypeEnum.LOGOUT_SELF.getType()); |
| 327 | reqDTO.setTraceId(TracerUtils.getTraceId()); | 250 | reqDTO.setTraceId(TracerUtils.getTraceId()); |
| 328 | reqDTO.setUserId(userId); | 251 | reqDTO.setUserId(userId); |
| 329 | reqDTO.setUserType(getUserType().getValue()); | 252 | reqDTO.setUserType(getUserType().getValue()); |
| 330 | - reqDTO.setUsername(username); | 253 | + reqDTO.setUsername(getMobile(userId)); |
| 331 | reqDTO.setUserAgent(ServletUtils.getUserAgent()); | 254 | reqDTO.setUserAgent(ServletUtils.getUserAgent()); |
| 332 | reqDTO.setUserIp(getClientIP()); | 255 | reqDTO.setUserIp(getClientIP()); |
| 333 | reqDTO.setResult(LoginResultEnum.SUCCESS.getResult()); | 256 | reqDTO.setResult(LoginResultEnum.SUCCESS.getResult()); |
| 334 | loginLogApi.createLoginLog(reqDTO); | 257 | loginLogApi.createLoginLog(reqDTO); |
| 335 | } | 258 | } |
| 336 | 259 | ||
| 260 | + private String getMobile(Long userId) { | ||
| 261 | + if (userId == null) { | ||
| 262 | + return null; | ||
| 263 | + } | ||
| 264 | + MemberUserDO user = userService.getUser(userId); | ||
| 265 | + return user != null ? user.getMobile() : null; | ||
| 266 | + } | ||
| 267 | + | ||
| 268 | + private UserTypeEnum getUserType() { | ||
| 269 | + return UserTypeEnum.MEMBER; | ||
| 270 | + } | ||
| 271 | + | ||
| 337 | } | 272 | } |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserService.java
| @@ -69,4 +69,13 @@ public interface MemberUserService { | @@ -69,4 +69,13 @@ public interface MemberUserService { | ||
| 69 | */ | 69 | */ |
| 70 | void updateUserMobile(Long userId, AppUserUpdateMobileReqVO reqVO); | 70 | void updateUserMobile(Long userId, AppUserUpdateMobileReqVO reqVO); |
| 71 | 71 | ||
| 72 | + /** | ||
| 73 | + * 判断密码是否匹配 | ||
| 74 | + * | ||
| 75 | + * @param rawPassword 未加密的密码 | ||
| 76 | + * @param encodedPassword 加密后的密码 | ||
| 77 | + * @return 是否匹配 | ||
| 78 | + */ | ||
| 79 | + boolean isPasswordMatch(String rawPassword, String encodedPassword); | ||
| 80 | + | ||
| 72 | } | 81 | } |
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java
| @@ -69,7 +69,7 @@ public class MemberUserServiceImpl implements MemberUserService { | @@ -69,7 +69,7 @@ public class MemberUserServiceImpl implements MemberUserService { | ||
| 69 | MemberUserDO user = new MemberUserDO(); | 69 | MemberUserDO user = new MemberUserDO(); |
| 70 | user.setMobile(mobile); | 70 | user.setMobile(mobile); |
| 71 | user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启 | 71 | user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启 |
| 72 | - user.setPassword(passwordEncoder.encode(password)); // 加密密码 | 72 | + user.setPassword(encodePassword(password)); // 加密密码 |
| 73 | user.setRegisterIp(registerIp); | 73 | user.setRegisterIp(registerIp); |
| 74 | memberUserMapper.insert(user); | 74 | memberUserMapper.insert(user); |
| 75 | return user; | 75 | return user; |
| @@ -127,6 +127,21 @@ public class MemberUserServiceImpl implements MemberUserService { | @@ -127,6 +127,21 @@ public class MemberUserServiceImpl implements MemberUserService { | ||
| 127 | memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(reqVO.getMobile()).build()); | 127 | memberUserMapper.updateById(MemberUserDO.builder().id(userId).mobile(reqVO.getMobile()).build()); |
| 128 | } | 128 | } |
| 129 | 129 | ||
| 130 | + @Override | ||
| 131 | + public boolean isPasswordMatch(String rawPassword, String encodedPassword) { | ||
| 132 | + return passwordEncoder.matches(rawPassword, encodedPassword); | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + /** | ||
| 136 | + * 对密码进行加密 | ||
| 137 | + * | ||
| 138 | + * @param password 密码 | ||
| 139 | + * @return 加密后的密码 | ||
| 140 | + */ | ||
| 141 | + private String encodePassword(String password) { | ||
| 142 | + return passwordEncoder.encode(password); | ||
| 143 | + } | ||
| 144 | + | ||
| 130 | @VisibleForTesting | 145 | @VisibleForTesting |
| 131 | public MemberUserDO checkUserExists(Long id) { | 146 | public MemberUserDO checkUserExists(Long id) { |
| 132 | if (id == null) { | 147 | if (id == null) { |
yudao-module-member/yudao-module-member-biz/src/test/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceTest.java
| @@ -9,14 +9,13 @@ import cn.iocoder.yudao.module.member.controller.app.auth.vo.AppAuthUpdatePasswo | @@ -9,14 +9,13 @@ import cn.iocoder.yudao.module.member.controller.app.auth.vo.AppAuthUpdatePasswo | ||
| 9 | import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; | 9 | import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; |
| 10 | import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; | 10 | import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper; |
| 11 | import cn.iocoder.yudao.module.member.service.user.MemberUserService; | 11 | import cn.iocoder.yudao.module.member.service.user.MemberUserService; |
| 12 | -import cn.iocoder.yudao.module.system.api.auth.UserSessionApi; | 12 | +import cn.iocoder.yudao.module.system.api.auth.OAuth2TokenApi; |
| 13 | import cn.iocoder.yudao.module.system.api.logger.LoginLogApi; | 13 | import cn.iocoder.yudao.module.system.api.logger.LoginLogApi; |
| 14 | import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; | 14 | import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; |
| 15 | import cn.iocoder.yudao.module.system.api.social.SocialUserApi; | 15 | import cn.iocoder.yudao.module.system.api.social.SocialUserApi; |
| 16 | import org.junit.jupiter.api.Test; | 16 | import org.junit.jupiter.api.Test; |
| 17 | import org.springframework.boot.test.mock.mockito.MockBean; | 17 | import org.springframework.boot.test.mock.mockito.MockBean; |
| 18 | import org.springframework.context.annotation.Import; | 18 | import org.springframework.context.annotation.Import; |
| 19 | -import org.springframework.security.authentication.AuthenticationManager; | ||
| 20 | import org.springframework.security.crypto.password.PasswordEncoder; | 19 | import org.springframework.security.crypto.password.PasswordEncoder; |
| 21 | 20 | ||
| 22 | import javax.annotation.Resource; | 21 | import javax.annotation.Resource; |
| @@ -38,8 +37,8 @@ import static org.mockito.Mockito.when; | @@ -38,8 +37,8 @@ import static org.mockito.Mockito.when; | ||
| 38 | @Import({MemberAuthServiceImpl.class, YudaoRedisAutoConfiguration.class}) | 37 | @Import({MemberAuthServiceImpl.class, YudaoRedisAutoConfiguration.class}) |
| 39 | public class MemberAuthServiceTest extends BaseDbAndRedisUnitTest { | 38 | public class MemberAuthServiceTest extends BaseDbAndRedisUnitTest { |
| 40 | 39 | ||
| 41 | - @MockBean | ||
| 42 | - private AuthenticationManager authenticationManager; | 40 | + // TODO @芋艿:登录相关的单测,待补全 |
| 41 | + | ||
| 43 | @MockBean | 42 | @MockBean |
| 44 | private MemberUserService userService; | 43 | private MemberUserService userService; |
| 45 | @MockBean | 44 | @MockBean |
| @@ -47,7 +46,7 @@ public class MemberAuthServiceTest extends BaseDbAndRedisUnitTest { | @@ -47,7 +46,7 @@ public class MemberAuthServiceTest extends BaseDbAndRedisUnitTest { | ||
| 47 | @MockBean | 46 | @MockBean |
| 48 | private LoginLogApi loginLogApi; | 47 | private LoginLogApi loginLogApi; |
| 49 | @MockBean | 48 | @MockBean |
| 50 | - private UserSessionApi userSessionApi; | 49 | + private OAuth2TokenApi oauth2TokenApi; |
| 51 | @MockBean | 50 | @MockBean |
| 52 | private SocialUserApi socialUserApi; | 51 | private SocialUserApi socialUserApi; |
| 53 | @MockBean | 52 | @MockBean |
yudao-module-system/yudao-module-system-api/pom.xml
| @@ -29,13 +29,6 @@ | @@ -29,13 +29,6 @@ | ||
| 29 | <optional>true</optional> | 29 | <optional>true</optional> |
| 30 | </dependency> | 30 | </dependency> |
| 31 | 31 | ||
| 32 | - <!-- 用户信息 --> | ||
| 33 | - <dependency> | ||
| 34 | - <groupId>cn.iocoder.boot</groupId> | ||
| 35 | - <artifactId>yudao-spring-boot-starter-security</artifactId> | ||
| 36 | - <optional>true</optional> | ||
| 37 | - </dependency> | ||
| 38 | - | ||
| 39 | </dependencies> | 32 | </dependencies> |
| 40 | 33 | ||
| 41 | </project> | 34 | </project> |
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApi.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.api.auth; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO; | ||
| 4 | +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCreateReqDTO; | ||
| 5 | +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO; | ||
| 6 | + | ||
| 7 | +import javax.validation.Valid; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * OAuth2.0 Token API 接口 | ||
| 11 | + * | ||
| 12 | + * @author 芋道源码 | ||
| 13 | + */ | ||
| 14 | +public interface OAuth2TokenApi { | ||
| 15 | + | ||
| 16 | + /** | ||
| 17 | + * 创建访问令牌 | ||
| 18 | + * | ||
| 19 | + * @param reqDTO 访问令牌的创建信息 | ||
| 20 | + * @return 访问令牌的信息 | ||
| 21 | + */ | ||
| 22 | + OAuth2AccessTokenRespDTO createAccessToken(@Valid OAuth2AccessTokenCreateReqDTO reqDTO); | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 校验访问令牌 | ||
| 26 | + * | ||
| 27 | + * @param accessToken 访问令牌 | ||
| 28 | + * @return 访问令牌的信息 | ||
| 29 | + */ | ||
| 30 | + OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken); | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 移除访问令牌 | ||
| 34 | + * | ||
| 35 | + * @param accessToken 访问令牌 | ||
| 36 | + * @return 访问令牌的信息 | ||
| 37 | + */ | ||
| 38 | + OAuth2AccessTokenRespDTO removeAccessToken(String accessToken); | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * 刷新访问令牌 | ||
| 42 | + * | ||
| 43 | + * @param refreshToken 刷新令牌 | ||
| 44 | + * @param clientId 客户端编号 | ||
| 45 | + * @return 访问令牌的信息 | ||
| 46 | + */ | ||
| 47 | + OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, Long clientId); | ||
| 48 | + | ||
| 49 | +} |
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/UserSessionApi.java deleted
| 1 | -package cn.iocoder.yudao.module.system.api.auth; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 4 | - | ||
| 5 | -import javax.validation.constraints.NotEmpty; | ||
| 6 | -import javax.validation.constraints.NotNull; | ||
| 7 | - | ||
| 8 | -/** | ||
| 9 | - * 在线用户 Session API 接口 | ||
| 10 | - * | ||
| 11 | - * @author 芋道源码 | ||
| 12 | - */ | ||
| 13 | -public interface UserSessionApi { | ||
| 14 | - | ||
| 15 | - /** | ||
| 16 | - * 创建在线用户 Session | ||
| 17 | - * | ||
| 18 | - * @param loginUser 登录用户 | ||
| 19 | - * @param userIp 用户 IP | ||
| 20 | - * @param userAgent 用户 UA | ||
| 21 | - * @return Token 令牌 | ||
| 22 | - */ | ||
| 23 | - String createUserSession(@NotNull(message = "登录用户不能为空") LoginUser loginUser, String userIp, String userAgent); | ||
| 24 | - | ||
| 25 | - /** | ||
| 26 | - * 刷新在线用户 Session 的更新时间 | ||
| 27 | - * | ||
| 28 | - * @param token Token 令牌 | ||
| 29 | - * @param loginUser 登录用户 | ||
| 30 | - */ | ||
| 31 | - void refreshUserSession(@NotEmpty(message = "Token 令牌不能为空") String token, | ||
| 32 | - @NotNull(message = "登录用户不能为空") LoginUser loginUser); | ||
| 33 | - | ||
| 34 | - /** | ||
| 35 | - * 删除在线用户 Session | ||
| 36 | - * | ||
| 37 | - * @param token Token 令牌 | ||
| 38 | - */ | ||
| 39 | - void deleteUserSession(String token); | ||
| 40 | - | ||
| 41 | - /** | ||
| 42 | - * 获得 Token 令牌对应的在线用户 | ||
| 43 | - * | ||
| 44 | - * @param token Token 令牌 | ||
| 45 | - * @return 在线用户 | ||
| 46 | - */ | ||
| 47 | - LoginUser getLoginUser(String token); | ||
| 48 | - | ||
| 49 | - /** | ||
| 50 | - * 获得 Session 超时时间,单位:毫秒 | ||
| 51 | - * | ||
| 52 | - * @return 超时时间 | ||
| 53 | - */ | ||
| 54 | - Long getSessionTimeoutMillis(); | ||
| 55 | - | ||
| 56 | -} |
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCheckRespDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.api.auth.dto; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import java.io.Serializable; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * OAuth2.0 访问令牌的校验 Response DTO | ||
| 9 | + * | ||
| 10 | + * @author 芋道源码 | ||
| 11 | + */ | ||
| 12 | +@Data | ||
| 13 | +public class OAuth2AccessTokenCheckRespDTO implements Serializable { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 用户编号 | ||
| 17 | + */ | ||
| 18 | + private Long userId; | ||
| 19 | + /** | ||
| 20 | + * 用户类型 | ||
| 21 | + */ | ||
| 22 | + private Integer userType; | ||
| 23 | + /** | ||
| 24 | + * 租户编号 | ||
| 25 | + */ | ||
| 26 | + private Long tenantId; | ||
| 27 | + | ||
| 28 | +} |
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenCreateReqDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.api.auth.dto; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||
| 4 | +import cn.iocoder.yudao.framework.common.validation.InEnum; | ||
| 5 | +import lombok.Data; | ||
| 6 | + | ||
| 7 | +import javax.validation.constraints.NotNull; | ||
| 8 | +import java.io.Serializable; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * OAuth2.0 访问令牌创建 Request DTO | ||
| 12 | + * | ||
| 13 | + * @author 芋道源码 | ||
| 14 | + */ | ||
| 15 | +@Data | ||
| 16 | +public class OAuth2AccessTokenCreateReqDTO implements Serializable { | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * 用户编号 | ||
| 20 | + */ | ||
| 21 | + @NotNull(message = "用户编号不能为空") | ||
| 22 | + private Long userId; | ||
| 23 | + /** | ||
| 24 | + * 用户类型 | ||
| 25 | + */ | ||
| 26 | + @NotNull(message = "用户类型不能为空") | ||
| 27 | + @InEnum(value = UserTypeEnum.class, message = "用户类型必须是 {value}") | ||
| 28 | + private Integer userType; | ||
| 29 | + /** | ||
| 30 | + * 客户端编号 | ||
| 31 | + */ | ||
| 32 | + @NotNull(message = "客户端编号不能为空") | ||
| 33 | + private Long clientId; | ||
| 34 | + | ||
| 35 | +} |
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/auth/dto/OAuth2AccessTokenRespDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.api.auth.dto; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | +import lombok.experimental.Accessors; | ||
| 5 | + | ||
| 6 | +import java.io.Serializable; | ||
| 7 | +import java.util.Date; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * OAuth2.0 访问令牌的信息 Response DTO | ||
| 11 | + * | ||
| 12 | + * @author 芋道源码 | ||
| 13 | + */ | ||
| 14 | +@Data | ||
| 15 | +@Accessors(chain = true) | ||
| 16 | +public class OAuth2AccessTokenRespDTO implements Serializable { | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * 访问令牌 | ||
| 20 | + */ | ||
| 21 | + private String accessToken; | ||
| 22 | + /** | ||
| 23 | + * 刷新令牌 | ||
| 24 | + */ | ||
| 25 | + private String refreshToken; | ||
| 26 | + /** | ||
| 27 | + * 用户编号 | ||
| 28 | + */ | ||
| 29 | + private Long userId; | ||
| 30 | + /** | ||
| 31 | + * 用户类型 | ||
| 32 | + */ | ||
| 33 | + private Integer userType; | ||
| 34 | + /** | ||
| 35 | + * 过期时间 | ||
| 36 | + */ | ||
| 37 | + private Date expiresTime; | ||
| 38 | + | ||
| 39 | +} |
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
| @@ -12,7 +12,6 @@ public interface ErrorCodeConstants { | @@ -12,7 +12,6 @@ public interface ErrorCodeConstants { | ||
| 12 | // ========== AUTH 模块 1002000000 ========== | 12 | // ========== AUTH 模块 1002000000 ========== |
| 13 | ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1002000000, "登录失败,账号密码不正确"); | 13 | ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1002000000, "登录失败,账号密码不正确"); |
| 14 | ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1002000001, "登录失败,账号被禁用"); | 14 | ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1002000001, "登录失败,账号被禁用"); |
| 15 | - ErrorCode AUTH_LOGIN_FAIL_UNKNOWN = new ErrorCode(1002000002, "登录失败"); // 登录失败的兜底,未知原因 | ||
| 16 | ErrorCode AUTH_LOGIN_CAPTCHA_NOT_FOUND = new ErrorCode(1002000003, "验证码不存在"); | 15 | ErrorCode AUTH_LOGIN_CAPTCHA_NOT_FOUND = new ErrorCode(1002000003, "验证码不存在"); |
| 17 | ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1002000004, "验证码不正确"); | 16 | ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1002000004, "验证码不正确"); |
| 18 | ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1002000005, "未绑定账号,需要进行绑定"); | 17 | ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1002000005, "未绑定账号,需要进行绑定"); |
| @@ -120,8 +119,11 @@ public interface ErrorCodeConstants { | @@ -120,8 +119,11 @@ public interface ErrorCodeConstants { | ||
| 120 | ErrorCode SOCIAL_USER_UNBIND_NOT_SELF = new ErrorCode(1002018001, "社交解绑失败,非当前用户绑定"); | 119 | ErrorCode SOCIAL_USER_UNBIND_NOT_SELF = new ErrorCode(1002018001, "社交解绑失败,非当前用户绑定"); |
| 121 | ErrorCode SOCIAL_USER_NOT_FOUND = new ErrorCode(1002018002, "社交授权失败,找不到对应的用户"); | 120 | ErrorCode SOCIAL_USER_NOT_FOUND = new ErrorCode(1002018002, "社交授权失败,找不到对应的用户"); |
| 122 | 121 | ||
| 123 | - // ========== 系统铭感词 1002019000 ========= | 122 | + // ========== 系统敏感词 1002019000 ========= |
| 124 | ErrorCode SENSITIVE_WORD_NOT_EXISTS = new ErrorCode(1002019000, "系统敏感词在所有标签中都不存在"); | 123 | ErrorCode SENSITIVE_WORD_NOT_EXISTS = new ErrorCode(1002019000, "系统敏感词在所有标签中都不存在"); |
| 125 | ErrorCode SENSITIVE_WORD_EXISTS = new ErrorCode(1002019001, "系统敏感词已在标签中存在"); | 124 | ErrorCode SENSITIVE_WORD_EXISTS = new ErrorCode(1002019001, "系统敏感词已在标签中存在"); |
| 126 | 125 | ||
| 126 | + // ========== 系统敏感词 1002020000 ========= | ||
| 127 | + ErrorCode OAUTH2_CLIENT_NOT_EXISTS = new ErrorCode(1002020000, "OAuth2 客户端不存在"); | ||
| 128 | + | ||
| 127 | } | 129 | } |
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/auth/OAuth2ClientIdEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.enums.auth; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Getter; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * OAuth2.0 客户端的编号枚举 | ||
| 8 | + */ | ||
| 9 | +@AllArgsConstructor | ||
| 10 | +@Getter | ||
| 11 | +public enum OAuth2ClientIdEnum { | ||
| 12 | + | ||
| 13 | + DEFAULT(1L); // 系统默认 | ||
| 14 | + | ||
| 15 | + private final Long id; | ||
| 16 | + | ||
| 17 | +} |
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/logger/LoginLogTypeEnum.java
| @@ -12,12 +12,10 @@ public enum LoginLogTypeEnum { | @@ -12,12 +12,10 @@ public enum LoginLogTypeEnum { | ||
| 12 | 12 | ||
| 13 | LOGIN_USERNAME(100), // 使用账号登录 | 13 | LOGIN_USERNAME(100), // 使用账号登录 |
| 14 | LOGIN_SOCIAL(101), // 使用社交登录 | 14 | LOGIN_SOCIAL(101), // 使用社交登录 |
| 15 | - LOGIN_MOCK(102), // 使用 Mock 登录 | ||
| 16 | LOGIN_MOBILE(103), // 使用手机登陆 | 15 | LOGIN_MOBILE(103), // 使用手机登陆 |
| 17 | LOGIN_SMS(104), // 使用短信登陆 | 16 | LOGIN_SMS(104), // 使用短信登陆 |
| 18 | 17 | ||
| 19 | LOGOUT_SELF(200), // 自己主动登出 | 18 | LOGOUT_SELF(200), // 自己主动登出 |
| 20 | - LOGOUT_TIMEOUT(201), // 超时登出 | ||
| 21 | LOGOUT_DELETE(202), // 强制退出 | 19 | LOGOUT_DELETE(202), // 强制退出 |
| 22 | ; | 20 | ; |
| 23 | 21 |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/OAuth2TokenApiImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.api.auth; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO; | ||
| 4 | +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCreateReqDTO; | ||
| 5 | +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO; | ||
| 6 | +import cn.iocoder.yudao.module.system.convert.auth.OAuth2TokenConvert; | ||
| 7 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; | ||
| 8 | +import cn.iocoder.yudao.module.system.service.auth.OAuth2TokenService; | ||
| 9 | +import org.springframework.stereotype.Service; | ||
| 10 | + | ||
| 11 | +import javax.annotation.Resource; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * OAuth2.0 Token API 实现类 | ||
| 15 | + * | ||
| 16 | + * @author 芋道源码 | ||
| 17 | + */ | ||
| 18 | +@Service | ||
| 19 | +public class OAuth2TokenApiImpl implements OAuth2TokenApi { | ||
| 20 | + | ||
| 21 | + @Resource | ||
| 22 | + private OAuth2TokenService oauth2TokenService; | ||
| 23 | + | ||
| 24 | + @Override | ||
| 25 | + public OAuth2AccessTokenRespDTO createAccessToken(OAuth2AccessTokenCreateReqDTO reqDTO) { | ||
| 26 | + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken( | ||
| 27 | + reqDTO.getUserId(), reqDTO.getUserType(), reqDTO.getClientId()); | ||
| 28 | + return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + @Override | ||
| 32 | + public OAuth2AccessTokenCheckRespDTO checkAccessToken(String accessToken) { | ||
| 33 | + return OAuth2TokenConvert.INSTANCE.convert(oauth2TokenService.checkAccessToken(accessToken)); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + @Override | ||
| 37 | + public OAuth2AccessTokenRespDTO removeAccessToken(String accessToken) { | ||
| 38 | + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(accessToken); | ||
| 39 | + return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO); | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + @Override | ||
| 43 | + public OAuth2AccessTokenRespDTO refreshAccessToken(String refreshToken, Long clientId) { | ||
| 44 | + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, clientId); | ||
| 45 | + return OAuth2TokenConvert.INSTANCE.convert2(accessTokenDO); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/auth/UserSessionApiImpl.java deleted
| 1 | -package cn.iocoder.yudao.module.system.api.auth; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 4 | -import cn.iocoder.yudao.module.system.service.auth.UserSessionService; | ||
| 5 | -import org.springframework.stereotype.Service; | ||
| 6 | -import org.springframework.validation.annotation.Validated; | ||
| 7 | - | ||
| 8 | -import javax.annotation.Resource; | ||
| 9 | - | ||
| 10 | -/** | ||
| 11 | - * 在线用户 Session API 实现类 | ||
| 12 | - * | ||
| 13 | - * @author 芋道源码 | ||
| 14 | - */ | ||
| 15 | -@Service | ||
| 16 | -@Validated | ||
| 17 | -public class UserSessionApiImpl implements UserSessionApi { | ||
| 18 | - | ||
| 19 | - @Resource | ||
| 20 | - private UserSessionService userSessionService; | ||
| 21 | - | ||
| 22 | - @Override | ||
| 23 | - public String createUserSession(LoginUser loginUser, String userIp, String userAgent) { | ||
| 24 | - return userSessionService.createUserSession(loginUser, userIp, userAgent); | ||
| 25 | - } | ||
| 26 | - | ||
| 27 | - @Override | ||
| 28 | - public void refreshUserSession(String token, LoginUser loginUser) { | ||
| 29 | - userSessionService.refreshUserSession(token, loginUser); | ||
| 30 | - } | ||
| 31 | - | ||
| 32 | - @Override | ||
| 33 | - public void deleteUserSession(String token) { | ||
| 34 | - userSessionService.deleteUserSession(token); | ||
| 35 | - } | ||
| 36 | - | ||
| 37 | - @Override | ||
| 38 | - public LoginUser getLoginUser(String token) { | ||
| 39 | - return userSessionService.getLoginUser(token); | ||
| 40 | - } | ||
| 41 | - | ||
| 42 | - @Override | ||
| 43 | - public Long getSessionTimeoutMillis() { | ||
| 44 | - return userSessionService.getSessionTimeoutMillis(); | ||
| 45 | - } | ||
| 46 | - | ||
| 47 | -} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.http
| 1 | ### 请求 /login 接口 => 成功 | 1 | ### 请求 /login 接口 => 成功 |
| 2 | -POST {{baseUrl}}/system/login | 2 | +POST {{baseUrl}}/system/auth/login |
| 3 | Content-Type: application/json | 3 | Content-Type: application/json |
| 4 | tenant-id: {{adminTenentId}} | 4 | tenant-id: {{adminTenentId}} |
| 5 | 5 | ||
| @@ -11,7 +11,7 @@ tenant-id: {{adminTenentId}} | @@ -11,7 +11,7 @@ tenant-id: {{adminTenentId}} | ||
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | ### 请求 /login 接口 => 成功(无验证码) | 13 | ### 请求 /login 接口 => 成功(无验证码) |
| 14 | -POST {{baseUrl}}/system/login | 14 | +POST {{baseUrl}}/system/auth/login |
| 15 | Content-Type: application/json | 15 | Content-Type: application/json |
| 16 | tenant-id: {{adminTenentId}} | 16 | tenant-id: {{adminTenentId}} |
| 17 | 17 | ||
| @@ -21,7 +21,7 @@ tenant-id: {{adminTenentId}} | @@ -21,7 +21,7 @@ tenant-id: {{adminTenentId}} | ||
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | ### 请求 /get-permission-info 接口 => 成功 | 23 | ### 请求 /get-permission-info 接口 => 成功 |
| 24 | -GET {{baseUrl}}/system/get-permission-info | 24 | +GET {{baseUrl}}/system/auth/get-permission-info |
| 25 | Authorization: Bearer {{token}} | 25 | Authorization: Bearer {{token}} |
| 26 | tenant-id: {{adminTenentId}} | 26 | tenant-id: {{adminTenentId}} |
| 27 | 27 |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java
| 1 | package cn.iocoder.yudao.module.system.controller.admin.auth; | 1 | package cn.iocoder.yudao.module.system.controller.admin.auth; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.util.StrUtil; | ||
| 3 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 4 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 5 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | import cn.iocoder.yudao.framework.common.util.collection.SetUtils; | 6 | import cn.iocoder.yudao.framework.common.util.collection.SetUtils; |
| 6 | import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; | 7 | import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; |
| 8 | +import cn.iocoder.yudao.framework.security.config.SecurityProperties; | ||
| 7 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; | 9 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; |
| 8 | import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; | 10 | import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; |
| 9 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; | 11 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; |
| 10 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; | 12 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; |
| 11 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | 13 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; |
| 14 | +import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; | ||
| 12 | import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; | 15 | import cn.iocoder.yudao.module.system.enums.permission.MenuTypeEnum; |
| 13 | import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; | 16 | import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; |
| 14 | import cn.iocoder.yudao.module.system.service.permission.PermissionService; | 17 | import cn.iocoder.yudao.module.system.service.permission.PermissionService; |
| @@ -24,18 +27,19 @@ import org.springframework.validation.annotation.Validated; | @@ -24,18 +27,19 @@ import org.springframework.validation.annotation.Validated; | ||
| 24 | import org.springframework.web.bind.annotation.*; | 27 | import org.springframework.web.bind.annotation.*; |
| 25 | 28 | ||
| 26 | import javax.annotation.Resource; | 29 | import javax.annotation.Resource; |
| 30 | +import javax.servlet.http.HttpServletRequest; | ||
| 27 | import javax.validation.Valid; | 31 | import javax.validation.Valid; |
| 28 | import java.util.List; | 32 | import java.util.List; |
| 33 | +import java.util.Set; | ||
| 29 | 34 | ||
| 30 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 35 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 31 | -import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; | ||
| 32 | -import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getUserAgent; | ||
| 33 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | 36 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| 34 | -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserRoleIds; | 37 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.obtainAuthorization; |
| 38 | +import static java.util.Collections.singleton; | ||
| 35 | 39 | ||
| 36 | @Api(tags = "管理后台 - 认证") | 40 | @Api(tags = "管理后台 - 认证") |
| 37 | @RestController | 41 | @RestController |
| 38 | -@RequestMapping("/system/auth") // 暂时不跟 /auth 结尾 | 42 | +@RequestMapping("/system/auth") |
| 39 | @Validated | 43 | @Validated |
| 40 | @Slf4j | 44 | @Slf4j |
| 41 | public class AuthController { | 45 | public class AuthController { |
| @@ -51,13 +55,33 @@ public class AuthController { | @@ -51,13 +55,33 @@ public class AuthController { | ||
| 51 | @Resource | 55 | @Resource |
| 52 | private SocialUserService socialUserService; | 56 | private SocialUserService socialUserService; |
| 53 | 57 | ||
| 58 | + @Resource | ||
| 59 | + private SecurityProperties securityProperties; | ||
| 60 | + | ||
| 54 | @PostMapping("/login") | 61 | @PostMapping("/login") |
| 55 | @ApiOperation("使用账号密码登录") | 62 | @ApiOperation("使用账号密码登录") |
| 56 | @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 | 63 | @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 |
| 57 | public CommonResult<AuthLoginRespVO> login(@RequestBody @Valid AuthLoginReqVO reqVO) { | 64 | public CommonResult<AuthLoginRespVO> login(@RequestBody @Valid AuthLoginReqVO reqVO) { |
| 58 | - String token = authService.login(reqVO, getClientIP(), getUserAgent()); | ||
| 59 | - // 返回结果 | ||
| 60 | - return success(AuthLoginRespVO.builder().token(token).build()); | 65 | + return success(authService.login(reqVO)); |
| 66 | + } | ||
| 67 | + | ||
| 68 | + @PostMapping("/logout") | ||
| 69 | + @ApiOperation("登出系统") | ||
| 70 | + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 | ||
| 71 | + public CommonResult<Boolean> logout(HttpServletRequest request) { | ||
| 72 | + String token = obtainAuthorization(request, securityProperties.getTokenHeader()); | ||
| 73 | + if (StrUtil.isNotBlank(token)) { | ||
| 74 | + authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType()); | ||
| 75 | + } | ||
| 76 | + return success(true); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + @PostMapping("/refresh-token") | ||
| 80 | + @ApiOperation("刷新令牌") | ||
| 81 | + @ApiImplicitParam(name = "refreshToken", value = "刷新令牌", required = true, dataTypeClass = String.class) | ||
| 82 | + @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 | ||
| 83 | + public CommonResult<AuthLoginRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken) { | ||
| 84 | + return success(authService.refreshToken(refreshToken)); | ||
| 61 | } | 85 | } |
| 62 | 86 | ||
| 63 | @GetMapping("/get-permission-info") | 87 | @GetMapping("/get-permission-info") |
| @@ -69,12 +93,12 @@ public class AuthController { | @@ -69,12 +93,12 @@ public class AuthController { | ||
| 69 | return null; | 93 | return null; |
| 70 | } | 94 | } |
| 71 | // 获得角色列表 | 95 | // 获得角色列表 |
| 72 | - List<RoleDO> roleList = roleService.getRolesFromCache(getLoginUserRoleIds()); | 96 | + Set<Long> roleIds = permissionService.getUserRoleIds(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus())); |
| 97 | + List<RoleDO> roleList = roleService.getRolesFromCache(roleIds); | ||
| 73 | // 获得菜单列表 | 98 | // 获得菜单列表 |
| 74 | - List<MenuDO> menuList = permissionService.getRoleMenuListFromCache( | ||
| 75 | - getLoginUserRoleIds(), // 注意,基于登录的角色,因为后续的权限判断也是基于它 | 99 | + List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(roleIds, |
| 76 | SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType(), MenuTypeEnum.BUTTON.getType()), | 100 | SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType(), MenuTypeEnum.BUTTON.getType()), |
| 77 | - SetUtils.asSet(CommonStatusEnum.ENABLE.getStatus())); | 101 | + singleton(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的 |
| 78 | // 拼接结果返回 | 102 | // 拼接结果返回 |
| 79 | return success(AuthConvert.INSTANCE.convert(user, roleList, menuList)); | 103 | return success(AuthConvert.INSTANCE.convert(user, roleList, menuList)); |
| 80 | } | 104 | } |
| @@ -82,11 +106,12 @@ public class AuthController { | @@ -82,11 +106,12 @@ public class AuthController { | ||
| 82 | @GetMapping("/list-menus") | 106 | @GetMapping("/list-menus") |
| 83 | @ApiOperation("获得登录用户的菜单列表") | 107 | @ApiOperation("获得登录用户的菜单列表") |
| 84 | public CommonResult<List<AuthMenuRespVO>> getMenus() { | 108 | public CommonResult<List<AuthMenuRespVO>> getMenus() { |
| 109 | + // 获得角色列表 | ||
| 110 | + Set<Long> roleIds = permissionService.getUserRoleIds(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus())); | ||
| 85 | // 获得用户拥有的菜单列表 | 111 | // 获得用户拥有的菜单列表 |
| 86 | - List<MenuDO> menuList = permissionService.getRoleMenuListFromCache( | ||
| 87 | - getLoginUserRoleIds(), // 注意,基于登录的角色,因为后续的权限判断也是基于它 | 112 | + List<MenuDO> menuList = permissionService.getRoleMenuListFromCache(roleIds, |
| 88 | SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()), // 只要目录和菜单类型 | 113 | SetUtils.asSet(MenuTypeEnum.DIR.getType(), MenuTypeEnum.MENU.getType()), // 只要目录和菜单类型 |
| 89 | - SetUtils.asSet(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的 | 114 | + singleton(CommonStatusEnum.ENABLE.getStatus())); // 只要开启的 |
| 90 | // 转换成 Tree 结构返回 | 115 | // 转换成 Tree 结构返回 |
| 91 | return success(AuthConvert.INSTANCE.buildMenuTree(menuList)); | 116 | return success(AuthConvert.INSTANCE.buildMenuTree(menuList)); |
| 92 | } | 117 | } |
| @@ -97,9 +122,7 @@ public class AuthController { | @@ -97,9 +122,7 @@ public class AuthController { | ||
| 97 | @ApiOperation("使用短信验证码登录") | 122 | @ApiOperation("使用短信验证码登录") |
| 98 | @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 | 123 | @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 |
| 99 | public CommonResult<AuthLoginRespVO> smsLogin(@RequestBody @Valid AuthSmsLoginReqVO reqVO) { | 124 | public CommonResult<AuthLoginRespVO> smsLogin(@RequestBody @Valid AuthSmsLoginReqVO reqVO) { |
| 100 | - String token = authService.smsLogin(reqVO, getClientIP(), getUserAgent()); | ||
| 101 | - // 返回结果 | ||
| 102 | - return success(AuthLoginRespVO.builder().token(token).build()); | 125 | + return success(authService.smsLogin(reqVO)); |
| 103 | } | 126 | } |
| 104 | 127 | ||
| 105 | @PostMapping("/send-sms-code") | 128 | @PostMapping("/send-sms-code") |
| @@ -127,18 +150,14 @@ public class AuthController { | @@ -127,18 +150,14 @@ public class AuthController { | ||
| 127 | @ApiOperation("社交快捷登录,使用 code 授权码") | 150 | @ApiOperation("社交快捷登录,使用 code 授权码") |
| 128 | @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 | 151 | @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 |
| 129 | public CommonResult<AuthLoginRespVO> socialQuickLogin(@RequestBody @Valid AuthSocialQuickLoginReqVO reqVO) { | 152 | public CommonResult<AuthLoginRespVO> socialQuickLogin(@RequestBody @Valid AuthSocialQuickLoginReqVO reqVO) { |
| 130 | - String token = authService.socialQuickLogin(reqVO, getClientIP(), getUserAgent()); | ||
| 131 | - // 返回结果 | ||
| 132 | - return success(AuthLoginRespVO.builder().token(token).build()); | 153 | + return success(authService.socialQuickLogin(reqVO)); |
| 133 | } | 154 | } |
| 134 | 155 | ||
| 135 | @PostMapping("/social-bind-login") | 156 | @PostMapping("/social-bind-login") |
| 136 | @ApiOperation("社交绑定登录,使用 code 授权码 + 账号密码") | 157 | @ApiOperation("社交绑定登录,使用 code 授权码 + 账号密码") |
| 137 | @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 | 158 | @OperateLog(enable = false) // 避免 Post 请求被记录操作日志 |
| 138 | public CommonResult<AuthLoginRespVO> socialBindLogin(@RequestBody @Valid AuthSocialBindLoginReqVO reqVO) { | 159 | public CommonResult<AuthLoginRespVO> socialBindLogin(@RequestBody @Valid AuthSocialBindLoginReqVO reqVO) { |
| 139 | - String token = authService.socialBindLogin(reqVO, getClientIP(), getUserAgent()); | ||
| 140 | - // 返回结果 | ||
| 141 | - return success(AuthLoginRespVO.builder().token(token).build()); | 160 | + return success(authService.socialBindLogin(reqVO)); |
| 142 | } | 161 | } |
| 143 | 162 | ||
| 144 | } | 163 | } |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2ClientController.java
0 → 100755
| 1 | +package cn.iocoder.yudao.module.system.controller.admin.auth; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 5 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientCreateReqVO; | ||
| 6 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientPageReqVO; | ||
| 7 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientRespVO; | ||
| 8 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientUpdateReqVO; | ||
| 9 | +import cn.iocoder.yudao.module.system.convert.auth.OAuth2ClientConvert; | ||
| 10 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; | ||
| 11 | +import cn.iocoder.yudao.module.system.service.auth.OAuth2ClientService; | ||
| 12 | +import io.swagger.annotations.Api; | ||
| 13 | +import io.swagger.annotations.ApiImplicitParam; | ||
| 14 | +import io.swagger.annotations.ApiOperation; | ||
| 15 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 16 | +import org.springframework.validation.annotation.Validated; | ||
| 17 | +import org.springframework.web.bind.annotation.*; | ||
| 18 | + | ||
| 19 | +import javax.annotation.Resource; | ||
| 20 | +import javax.validation.Valid; | ||
| 21 | + | ||
| 22 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | ||
| 23 | + | ||
| 24 | +@Api(tags = "管理后台 - OAuth2 客户端") | ||
| 25 | +@RestController | ||
| 26 | +@RequestMapping("/system/oauth2-client") | ||
| 27 | +@Validated | ||
| 28 | +public class OAuth2ClientController { | ||
| 29 | + | ||
| 30 | + @Resource | ||
| 31 | + private OAuth2ClientService oAuth2ClientService; | ||
| 32 | + | ||
| 33 | + @PostMapping("/create") | ||
| 34 | + @ApiOperation("创建 OAuth2 客户端") | ||
| 35 | + @PreAuthorize("@ss.hasPermission('system:oauth2-client:create')") | ||
| 36 | + public CommonResult<Long> createOAuth2Client(@Valid @RequestBody OAuth2ClientCreateReqVO createReqVO) { | ||
| 37 | + return success(oAuth2ClientService.createOAuth2Client(createReqVO)); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + @PutMapping("/update") | ||
| 41 | + @ApiOperation("更新 OAuth2 客户端") | ||
| 42 | + @PreAuthorize("@ss.hasPermission('system:oauth2-client:update')") | ||
| 43 | + public CommonResult<Boolean> updateOAuth2Client(@Valid @RequestBody OAuth2ClientUpdateReqVO updateReqVO) { | ||
| 44 | + oAuth2ClientService.updateOAuth2Client(updateReqVO); | ||
| 45 | + return success(true); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + @DeleteMapping("/delete") | ||
| 49 | + @ApiOperation("删除 OAuth2 客户端") | ||
| 50 | + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class) | ||
| 51 | + @PreAuthorize("@ss.hasPermission('system:oauth2-client:delete')") | ||
| 52 | + public CommonResult<Boolean> deleteOAuth2Client(@RequestParam("id") Long id) { | ||
| 53 | + oAuth2ClientService.deleteOAuth2Client(id); | ||
| 54 | + return success(true); | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + @GetMapping("/get") | ||
| 58 | + @ApiOperation("获得 OAuth2 客户端") | ||
| 59 | + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class) | ||
| 60 | + @PreAuthorize("@ss.hasPermission('system:oauth2-client:query')") | ||
| 61 | + public CommonResult<OAuth2ClientRespVO> getOAuth2Client(@RequestParam("id") Long id) { | ||
| 62 | + OAuth2ClientDO oAuth2Client = oAuth2ClientService.getOAuth2Client(id); | ||
| 63 | + return success(OAuth2ClientConvert.INSTANCE.convert(oAuth2Client)); | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + @GetMapping("/page") | ||
| 67 | + @ApiOperation("获得OAuth2 客户端分页") | ||
| 68 | + @PreAuthorize("@ss.hasPermission('system:oauth2-client:query')") | ||
| 69 | + public CommonResult<PageResult<OAuth2ClientRespVO>> getOAuth2ClientPage(@Valid OAuth2ClientPageReqVO pageVO) { | ||
| 70 | + PageResult<OAuth2ClientDO> pageResult = oAuth2ClientService.getOAuth2ClientPage(pageVO); | ||
| 71 | + return success(OAuth2ClientConvert.INSTANCE.convertPage(pageResult)); | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2Controller.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.controller.admin.auth; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.Api; | ||
| 4 | +import lombok.extern.slf4j.Slf4j; | ||
| 5 | +import org.springframework.validation.annotation.Validated; | ||
| 6 | +import org.springframework.web.bind.annotation.RequestMapping; | ||
| 7 | +import org.springframework.web.bind.annotation.RestController; | ||
| 8 | + | ||
| 9 | +@Api(tags = "管理后台 - OAuth2.0 授权") | ||
| 10 | +@RestController | ||
| 11 | +@RequestMapping("/system/oauth2") | ||
| 12 | +@Validated | ||
| 13 | +@Slf4j | ||
| 14 | +public class OAuth2Controller { | ||
| 15 | + | ||
| 16 | +// POST oauth/token TokenEndpoint:Password、Implicit、Code、Refresh Token | ||
| 17 | + | ||
| 18 | +// POST oauth/check_token CheckTokenEndpoint | ||
| 19 | + | ||
| 20 | +// DELETE oauth/token ConsumerTokenServices#revokeToken | ||
| 21 | + | ||
| 22 | +// GET oauth/authorize AuthorizationEndpoint | ||
| 23 | + | ||
| 24 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/OAuth2TokenController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.controller.admin.auth; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 5 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO; | ||
| 6 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenRespVO; | ||
| 7 | +import cn.iocoder.yudao.module.system.convert.auth.OAuth2TokenConvert; | ||
| 8 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; | ||
| 9 | +import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; | ||
| 10 | +import cn.iocoder.yudao.module.system.service.auth.AdminAuthService; | ||
| 11 | +import cn.iocoder.yudao.module.system.service.auth.OAuth2TokenService; | ||
| 12 | +import io.swagger.annotations.Api; | ||
| 13 | +import io.swagger.annotations.ApiImplicitParam; | ||
| 14 | +import io.swagger.annotations.ApiOperation; | ||
| 15 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 16 | +import org.springframework.web.bind.annotation.*; | ||
| 17 | + | ||
| 18 | +import javax.annotation.Resource; | ||
| 19 | +import javax.validation.Valid; | ||
| 20 | + | ||
| 21 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | ||
| 22 | + | ||
| 23 | +@Api(tags = "管理后台 - OAuth2.0 令牌") | ||
| 24 | +@RestController | ||
| 25 | +@RequestMapping("/system/oauth2-token") | ||
| 26 | +public class OAuth2TokenController { | ||
| 27 | + | ||
| 28 | + @Resource | ||
| 29 | + private OAuth2TokenService oauth2TokenService; | ||
| 30 | + @Resource | ||
| 31 | + private AdminAuthService authService; | ||
| 32 | + | ||
| 33 | + @GetMapping("/page") | ||
| 34 | + @ApiOperation(value = "获得访问令牌分页", notes = "只返回有效期内的") | ||
| 35 | + @PreAuthorize("@ss.hasPermission('system:oauth2-token:page')") | ||
| 36 | + public CommonResult<PageResult<OAuth2AccessTokenRespVO>> getAccessTokenPage(@Valid OAuth2AccessTokenPageReqVO reqVO) { | ||
| 37 | + PageResult<OAuth2AccessTokenDO> pageResult = oauth2TokenService.getAccessTokenPage(reqVO); | ||
| 38 | + return success(OAuth2TokenConvert.INSTANCE.convert(pageResult)); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + @DeleteMapping("/delete") | ||
| 42 | + @ApiOperation("删除访问令牌") | ||
| 43 | + @ApiImplicitParam(name = "accessToken", value = "访问令牌", required = true, dataTypeClass = String.class, example = "tudou") | ||
| 44 | + @PreAuthorize("@ss.hasPermission('system:oauth2-token:delete')") | ||
| 45 | + public CommonResult<Boolean> deleteAccessToken(@RequestParam("accessToken") String accessToken) { | ||
| 46 | + authService.logout(accessToken, LoginLogTypeEnum.LOGOUT_DELETE.getType()); | ||
| 47 | + return success(true); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/UserSessionController.java deleted
| 1 | -package cn.iocoder.yudao.module.system.controller.admin.auth; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageItemRespVO; | ||
| 4 | -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO; | ||
| 5 | -import cn.iocoder.yudao.module.system.convert.auth.UserSessionConvert; | ||
| 6 | -import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO; | ||
| 7 | -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; | ||
| 8 | -import cn.iocoder.yudao.module.system.service.auth.UserSessionService; | ||
| 9 | -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | ||
| 10 | -import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 11 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 12 | -import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | ||
| 13 | -import cn.iocoder.yudao.module.system.service.dept.DeptService; | ||
| 14 | -import cn.iocoder.yudao.module.system.service.user.AdminUserService; | ||
| 15 | -import io.swagger.annotations.Api; | ||
| 16 | -import io.swagger.annotations.ApiImplicitParam; | ||
| 17 | -import io.swagger.annotations.ApiOperation; | ||
| 18 | -import org.springframework.security.access.prepost.PreAuthorize; | ||
| 19 | -import org.springframework.validation.annotation.Validated; | ||
| 20 | -import org.springframework.web.bind.annotation.*; | ||
| 21 | - | ||
| 22 | -import javax.annotation.Resource; | ||
| 23 | -import java.util.ArrayList; | ||
| 24 | -import java.util.List; | ||
| 25 | -import java.util.Map; | ||
| 26 | - | ||
| 27 | -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | ||
| 28 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | ||
| 29 | - | ||
| 30 | -@Api(tags = "管理后台 - 用户 Session") | ||
| 31 | -@RestController | ||
| 32 | -@RequestMapping("/system/user-session") | ||
| 33 | -public class UserSessionController { | ||
| 34 | - | ||
| 35 | - @Resource | ||
| 36 | - private UserSessionService userSessionService; | ||
| 37 | - @Resource | ||
| 38 | - private AdminUserService userService; | ||
| 39 | - | ||
| 40 | - @Resource | ||
| 41 | - private DeptService deptService; | ||
| 42 | - | ||
| 43 | - @GetMapping("/page") | ||
| 44 | - @ApiOperation("获得 Session 分页列表") | ||
| 45 | - @PreAuthorize("@ss.hasPermission('system:user-session:page')") | ||
| 46 | - public CommonResult<PageResult<UserSessionPageItemRespVO>> getUserSessionPage(@Validated UserSessionPageReqVO reqVO) { | ||
| 47 | - // 获得 Session 分页 | ||
| 48 | - PageResult<UserSessionDO> pageResult = userSessionService.getUserSessionPage(reqVO); | ||
| 49 | - | ||
| 50 | - // 获得拼接需要的数据 | ||
| 51 | - Map<Long, AdminUserDO> userMap = userService.getUserMap( | ||
| 52 | - convertList(pageResult.getList(), UserSessionDO::getUserId)); | ||
| 53 | - Map<Long, DeptDO> deptMap = deptService.getDeptMap( | ||
| 54 | - convertList(userMap.values(), AdminUserDO::getDeptId)); | ||
| 55 | - // 拼接结果返回 | ||
| 56 | - List<UserSessionPageItemRespVO> sessionList = new ArrayList<>(pageResult.getList().size()); | ||
| 57 | - pageResult.getList().forEach(session -> { | ||
| 58 | - UserSessionPageItemRespVO respVO = UserSessionConvert.INSTANCE.convert(session); | ||
| 59 | - sessionList.add(respVO); | ||
| 60 | - // 设置用户账号 | ||
| 61 | - MapUtils.findAndThen(userMap, session.getUserId(), user -> { | ||
| 62 | - respVO.setUsername(user.getUsername()); | ||
| 63 | - // 设置用户部门 | ||
| 64 | - MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> respVO.setDeptName(dept.getName())); | ||
| 65 | - }); | ||
| 66 | - }); | ||
| 67 | - return success(new PageResult<>(sessionList, pageResult.getTotal())); | ||
| 68 | - } | ||
| 69 | - | ||
| 70 | - @DeleteMapping("/delete") | ||
| 71 | - @ApiOperation("删除 Session") | ||
| 72 | - @ApiImplicitParam(name = "id", value = "Session 编号", required = true, dataTypeClass = Long.class, example = "1024") | ||
| 73 | - @PreAuthorize("@ss.hasPermission('system:user-session:delete')") | ||
| 74 | - public CommonResult<Boolean> deleteUserSession(@RequestParam("id") Long id) { | ||
| 75 | - userSessionService.deleteUserSession(id); | ||
| 76 | - return success(true); | ||
| 77 | - } | ||
| 78 | - | ||
| 79 | -} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/auth/AuthLoginRespVO.java
| @@ -7,14 +7,25 @@ import lombok.Builder; | @@ -7,14 +7,25 @@ import lombok.Builder; | ||
| 7 | import lombok.Data; | 7 | import lombok.Data; |
| 8 | import lombok.NoArgsConstructor; | 8 | import lombok.NoArgsConstructor; |
| 9 | 9 | ||
| 10 | -@ApiModel("管理后台 - 账号密码登录 Response VO") | 10 | +import java.util.Date; |
| 11 | + | ||
| 12 | +@ApiModel("管理后台 - 登录 Response VO") | ||
| 11 | @Data | 13 | @Data |
| 12 | @NoArgsConstructor | 14 | @NoArgsConstructor |
| 13 | @AllArgsConstructor | 15 | @AllArgsConstructor |
| 14 | @Builder | 16 | @Builder |
| 15 | public class AuthLoginRespVO { | 17 | public class AuthLoginRespVO { |
| 16 | 18 | ||
| 17 | - @ApiModelProperty(value = "token", required = true, example = "yudaoyuanma") | ||
| 18 | - private String token; | 19 | + @ApiModelProperty(value = "用户编号", required = true, example = "1024") |
| 20 | + private Long userId; | ||
| 21 | + | ||
| 22 | + @ApiModelProperty(value = "访问令牌", required = true, example = "happy") | ||
| 23 | + private String accessToken; | ||
| 24 | + | ||
| 25 | + @ApiModelProperty(value = "刷新令牌", required = true, example = "nice") | ||
| 26 | + private String refreshToken; | ||
| 27 | + | ||
| 28 | + @ApiModelProperty(value = "过期时间", required = true) | ||
| 29 | + private Date expiresTime; | ||
| 19 | 30 | ||
| 20 | } | 31 | } |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientBaseVO.java
0 → 100755
| 1 | +package cn.iocoder.yudao.module.system.controller.admin.auth.vo.client; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModelProperty; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +import javax.validation.constraints.NotNull; | ||
| 7 | +import java.util.List; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | +* OAuth2 客户端 Base VO,提供给添加、修改、详细的子 VO 使用 | ||
| 11 | +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 | ||
| 12 | +*/ | ||
| 13 | +@Data | ||
| 14 | +public class OAuth2ClientBaseVO { | ||
| 15 | + | ||
| 16 | + @ApiModelProperty(value = "客户端编号", required = true) | ||
| 17 | + @NotNull(message = "客户端编号不能为空") | ||
| 18 | + private Long id; | ||
| 19 | + | ||
| 20 | + @ApiModelProperty(value = "客户端密钥", required = true) | ||
| 21 | + @NotNull(message = "客户端密钥不能为空") | ||
| 22 | + private String secret; | ||
| 23 | + | ||
| 24 | + @ApiModelProperty(value = "应用名", required = true) | ||
| 25 | + @NotNull(message = "应用名不能为空") | ||
| 26 | + private String name; | ||
| 27 | + | ||
| 28 | + @ApiModelProperty(value = "应用图标", required = true) | ||
| 29 | + @NotNull(message = "应用图标不能为空") | ||
| 30 | + private String logo; | ||
| 31 | + | ||
| 32 | + @ApiModelProperty(value = "应用描述") | ||
| 33 | + private String description; | ||
| 34 | + | ||
| 35 | + @ApiModelProperty(value = "状态", required = true) | ||
| 36 | + @NotNull(message = "状态不能为空") | ||
| 37 | + private Integer status; | ||
| 38 | + | ||
| 39 | + @ApiModelProperty(value = "访问令牌的有效期", required = true) | ||
| 40 | + @NotNull(message = "访问令牌的有效期不能为空") | ||
| 41 | + private Integer accessTokenValiditySeconds; | ||
| 42 | + | ||
| 43 | + @ApiModelProperty(value = "刷新令牌的有效期", required = true) | ||
| 44 | + @NotNull(message = "刷新令牌的有效期不能为空") | ||
| 45 | + private Integer refreshTokenValiditySeconds; | ||
| 46 | + | ||
| 47 | + @ApiModelProperty(value = "可重定向的 URI 地址", required = true) | ||
| 48 | + @NotNull(message = "可重定向的 URI 地址不能为空") | ||
| 49 | + private List<String> redirectUris; | ||
| 50 | + | ||
| 51 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientCreateReqVO.java
0 → 100755
| 1 | +package cn.iocoder.yudao.module.system.controller.admin.auth.vo.client; | ||
| 2 | + | ||
| 3 | +import lombok.*; | ||
| 4 | +import io.swagger.annotations.*; | ||
| 5 | + | ||
| 6 | +@ApiModel("管理后台 - OAuth2 客户端创建 Request VO") | ||
| 7 | +@Data | ||
| 8 | +@EqualsAndHashCode(callSuper = true) | ||
| 9 | +@ToString(callSuper = true) | ||
| 10 | +public class OAuth2ClientCreateReqVO extends OAuth2ClientBaseVO { | ||
| 11 | + | ||
| 12 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientPageReqVO.java
0 → 100755
| 1 | +package cn.iocoder.yudao.module.system.controller.admin.auth.vo.client; | ||
| 2 | + | ||
| 3 | +import lombok.*; | ||
| 4 | +import io.swagger.annotations.*; | ||
| 5 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | ||
| 6 | + | ||
| 7 | +@ApiModel("管理后台 - OAuth2 客户端分页 Request VO") | ||
| 8 | +@Data | ||
| 9 | +@EqualsAndHashCode(callSuper = true) | ||
| 10 | +@ToString(callSuper = true) | ||
| 11 | +public class OAuth2ClientPageReqVO extends PageParam { | ||
| 12 | + | ||
| 13 | + @ApiModelProperty(value = "应用名") | ||
| 14 | + private String name; | ||
| 15 | + | ||
| 16 | + @ApiModelProperty(value = "状态") | ||
| 17 | + private Integer status; | ||
| 18 | + | ||
| 19 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientRespVO.java
0 → 100755
| 1 | +package cn.iocoder.yudao.module.system.controller.admin.auth.vo.client; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import io.swagger.annotations.ApiModelProperty; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import lombok.EqualsAndHashCode; | ||
| 7 | +import lombok.ToString; | ||
| 8 | + | ||
| 9 | +import java.util.Date; | ||
| 10 | + | ||
| 11 | +@ApiModel("管理后台 - OAuth2 客户端 Response VO") | ||
| 12 | +@Data | ||
| 13 | +@EqualsAndHashCode(callSuper = true) | ||
| 14 | +@ToString(callSuper = true) | ||
| 15 | +public class OAuth2ClientRespVO extends OAuth2ClientBaseVO { | ||
| 16 | + | ||
| 17 | + @ApiModelProperty(value = "创建时间", required = true) | ||
| 18 | + private Date createTime; | ||
| 19 | + | ||
| 20 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/client/OAuth2ClientUpdateReqVO.java
0 → 100755
| 1 | +package cn.iocoder.yudao.module.system.controller.admin.auth.vo.client; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import lombok.EqualsAndHashCode; | ||
| 6 | +import lombok.ToString; | ||
| 7 | + | ||
| 8 | +@ApiModel("管理后台 - OAuth2 客户端更新 Request VO") | ||
| 9 | +@Data | ||
| 10 | +@EqualsAndHashCode(callSuper = true) | ||
| 11 | +@ToString(callSuper = true) | ||
| 12 | +public class OAuth2ClientUpdateReqVO extends OAuth2ClientBaseVO { | ||
| 13 | + | ||
| 14 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/session/UserSessionPageItemRespVO.java deleted
| 1 | -package cn.iocoder.yudao.module.system.controller.admin.auth.vo.session; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.common.pojo.PageParam; | ||
| 4 | -import io.swagger.annotations.ApiModel; | ||
| 5 | -import io.swagger.annotations.ApiModelProperty; | ||
| 6 | -import lombok.AllArgsConstructor; | ||
| 7 | -import lombok.Data; | ||
| 8 | -import lombok.EqualsAndHashCode; | ||
| 9 | -import lombok.NoArgsConstructor; | ||
| 10 | - | ||
| 11 | -import java.util.Date; | ||
| 12 | - | ||
| 13 | -@ApiModel(value = "管理后台 - 用户在线 Session Response VO", description = "相比用户基本信息来说,会多部门、用户账号等信息") | ||
| 14 | -@Data | ||
| 15 | -@NoArgsConstructor | ||
| 16 | -@AllArgsConstructor | ||
| 17 | -@EqualsAndHashCode(callSuper = true) | ||
| 18 | -public class UserSessionPageItemRespVO extends PageParam { | ||
| 19 | - | ||
| 20 | - @ApiModelProperty(value = "Session 编号", required = true, example = "fe50b9f6-d177-44b1-8da9-72ea34f63db7") | ||
| 21 | - private String id; | ||
| 22 | - | ||
| 23 | - @ApiModelProperty(value = "用户 IP", required = true, example = "127.0.0.1") | ||
| 24 | - private String userIp; | ||
| 25 | - | ||
| 26 | - @ApiModelProperty(value = "浏览器 UserAgent", required = true, example = "Mozilla/5.0") | ||
| 27 | - private String userAgent; | ||
| 28 | - | ||
| 29 | - @ApiModelProperty(value = "登录时间", required = true) | ||
| 30 | - private Date createTime; | ||
| 31 | - | ||
| 32 | - @ApiModelProperty(value = "用户账号", required = true, example = "yudao") | ||
| 33 | - private String username; | ||
| 34 | - | ||
| 35 | - @ApiModelProperty(value = "部门名称", example = "研发部") | ||
| 36 | - private String deptName; | ||
| 37 | - | ||
| 38 | -} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/session/UserSessionPageReqVO.java deleted
| 1 | -package cn.iocoder.yudao.module.system.controller.admin.auth.vo.session; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.common.pojo.PageParam; | ||
| 4 | -import io.swagger.annotations.ApiModel; | ||
| 5 | -import io.swagger.annotations.ApiModelProperty; | ||
| 6 | -import lombok.Data; | ||
| 7 | -import lombok.EqualsAndHashCode; | ||
| 8 | - | ||
| 9 | -@ApiModel("管理后台 - 在线用户 Session 分页 Request VO") | ||
| 10 | -@Data | ||
| 11 | -@EqualsAndHashCode(callSuper = true) | ||
| 12 | -public class UserSessionPageReqVO extends PageParam { | ||
| 13 | - | ||
| 14 | - @ApiModelProperty(value = "用户 IP", example = "127.0.0.1", notes = "模糊匹配") | ||
| 15 | - private String userIp; | ||
| 16 | - | ||
| 17 | - @ApiModelProperty(value = "用户账号", example = "yudao", notes = "模糊匹配") | ||
| 18 | - private String username; | ||
| 19 | - | ||
| 20 | -} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/token/OAuth2AccessTokenPageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.controller.admin.auth.vo.token; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | ||
| 4 | +import io.swagger.annotations.ApiModel; | ||
| 5 | +import io.swagger.annotations.ApiModelProperty; | ||
| 6 | +import lombok.Data; | ||
| 7 | +import lombok.EqualsAndHashCode; | ||
| 8 | + | ||
| 9 | +@ApiModel("管理后台 - 访问令牌分页 Request VO") | ||
| 10 | +@Data | ||
| 11 | +@EqualsAndHashCode(callSuper = true) | ||
| 12 | +public class OAuth2AccessTokenPageReqVO extends PageParam { | ||
| 13 | + | ||
| 14 | + @ApiModelProperty(value = "用户编号", required = true, example = "666") | ||
| 15 | + private Long userId; | ||
| 16 | + | ||
| 17 | + @ApiModelProperty(value = "用户类型", required = true, example = "2", notes = "参见 UserTypeEnum 枚举") | ||
| 18 | + private Integer userType; | ||
| 19 | + | ||
| 20 | + @ApiModelProperty(value = "客户端编号", required = true, example = "2") | ||
| 21 | + private Long clientId; | ||
| 22 | + | ||
| 23 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/vo/token/OAuth2AccessTokenRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.controller.admin.auth.vo.token; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import io.swagger.annotations.ApiModelProperty; | ||
| 5 | +import lombok.AllArgsConstructor; | ||
| 6 | +import lombok.Data; | ||
| 7 | +import lombok.NoArgsConstructor; | ||
| 8 | + | ||
| 9 | +import java.util.Date; | ||
| 10 | + | ||
| 11 | +@ApiModel("管理后台 - 访问令牌 Response VO") | ||
| 12 | +@Data | ||
| 13 | +@NoArgsConstructor | ||
| 14 | +@AllArgsConstructor | ||
| 15 | +public class OAuth2AccessTokenRespVO { | ||
| 16 | + | ||
| 17 | + @ApiModelProperty(value = "编号", required = true, example = "1024") | ||
| 18 | + private Long id; | ||
| 19 | + | ||
| 20 | + @ApiModelProperty(value = "访问令牌", required = true, example = "tudou") | ||
| 21 | + private String accessToken; | ||
| 22 | + | ||
| 23 | + @ApiModelProperty(value = "刷新令牌", required = true, example = "nice") | ||
| 24 | + private String refreshToken; | ||
| 25 | + | ||
| 26 | + @ApiModelProperty(value = "用户编号", required = true, example = "666") | ||
| 27 | + private Long userId; | ||
| 28 | + | ||
| 29 | + @ApiModelProperty(value = "用户类型", required = true, example = "2", notes = "参见 UserTypeEnum 枚举") | ||
| 30 | + private Integer userType; | ||
| 31 | + | ||
| 32 | + @ApiModelProperty(value = "客户端编号", required = true, example = "2") | ||
| 33 | + private Long clientId; | ||
| 34 | + | ||
| 35 | + @ApiModelProperty(value = "创建时间", required = true) | ||
| 36 | + private Date createTime; | ||
| 37 | + | ||
| 38 | + @ApiModelProperty(value = "过期时间", required = true) | ||
| 39 | + private Date expiresTime; | ||
| 40 | + | ||
| 41 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/AuthConvert.java
| 1 | package cn.iocoder.yudao.module.system.convert.auth; | 1 | package cn.iocoder.yudao.module.system.convert.auth; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||
| 4 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | 3 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 5 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 6 | -import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; | ||
| 7 | import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; | 4 | import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO; |
| 8 | import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; | 5 | import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO; |
| 9 | import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; | 6 | import cn.iocoder.yudao.module.system.api.social.dto.SocialUserBindReqDTO; |
| 10 | -import cn.iocoder.yudao.module.system.api.social.dto.SocialUserUnbindReqDTO; | ||
| 11 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; | 7 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; |
| 8 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; | ||
| 12 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; | 9 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; |
| 13 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; | 10 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; |
| 14 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | 11 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; |
| 15 | import cn.iocoder.yudao.module.system.enums.permission.MenuIdEnum; | 12 | import cn.iocoder.yudao.module.system.enums.permission.MenuIdEnum; |
| 16 | import org.mapstruct.Mapper; | 13 | import org.mapstruct.Mapper; |
| 17 | -import org.mapstruct.Mapping; | ||
| 18 | import org.mapstruct.factory.Mappers; | 14 | import org.mapstruct.factory.Mappers; |
| 19 | import org.slf4j.LoggerFactory; | 15 | import org.slf4j.LoggerFactory; |
| 20 | 16 | ||
| @@ -25,13 +21,7 @@ public interface AuthConvert { | @@ -25,13 +21,7 @@ public interface AuthConvert { | ||
| 25 | 21 | ||
| 26 | AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class); | 22 | AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class); |
| 27 | 23 | ||
| 28 | - @Mapping(source = "updateTime", target = "updateTime", ignore = true) // 字段相同,但是含义不同,忽略 | ||
| 29 | - LoginUser convert0(AdminUserDO bean); | ||
| 30 | - | ||
| 31 | - default LoginUser convert(AdminUserDO bean) { | ||
| 32 | - // 目的,为了设置 UserTypeEnum.ADMIN.getValue() | ||
| 33 | - return convert0(bean).setUserType(UserTypeEnum.ADMIN.getValue()); | ||
| 34 | - } | 24 | + AuthLoginRespVO convert(OAuth2AccessTokenDO bean); |
| 35 | 25 | ||
| 36 | default AuthPermissionInfoRespVO convert(AdminUserDO user, List<RoleDO> roleList, List<MenuDO> menuList) { | 26 | default AuthPermissionInfoRespVO convert(AdminUserDO user, List<RoleDO> roleList, List<MenuDO> menuList) { |
| 37 | return AuthPermissionInfoRespVO.builder() | 27 | return AuthPermissionInfoRespVO.builder() |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2ClientConvert.java
0 → 100755
| 1 | +package cn.iocoder.yudao.module.system.convert.auth; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientCreateReqVO; | ||
| 5 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientRespVO; | ||
| 6 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientUpdateReqVO; | ||
| 7 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; | ||
| 8 | +import org.mapstruct.Mapper; | ||
| 9 | +import org.mapstruct.factory.Mappers; | ||
| 10 | + | ||
| 11 | +import java.util.List; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * OAuth2 客户端 Convert | ||
| 15 | + * | ||
| 16 | + * @author 芋道源码 | ||
| 17 | + */ | ||
| 18 | +@Mapper | ||
| 19 | +public interface OAuth2ClientConvert { | ||
| 20 | + | ||
| 21 | + OAuth2ClientConvert INSTANCE = Mappers.getMapper(OAuth2ClientConvert.class); | ||
| 22 | + | ||
| 23 | + OAuth2ClientDO convert(OAuth2ClientCreateReqVO bean); | ||
| 24 | + | ||
| 25 | + OAuth2ClientDO convert(OAuth2ClientUpdateReqVO bean); | ||
| 26 | + | ||
| 27 | + OAuth2ClientRespVO convert(OAuth2ClientDO bean); | ||
| 28 | + | ||
| 29 | + List<OAuth2ClientRespVO> convertList(List<OAuth2ClientDO> list); | ||
| 30 | + | ||
| 31 | + PageResult<OAuth2ClientRespVO> convertPage(PageResult<OAuth2ClientDO> page); | ||
| 32 | + | ||
| 33 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/OAuth2TokenConvert.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.convert.auth; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenCheckRespDTO; | ||
| 5 | +import cn.iocoder.yudao.module.system.api.auth.dto.OAuth2AccessTokenRespDTO; | ||
| 6 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenRespVO; | ||
| 7 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; | ||
| 8 | +import org.mapstruct.Mapper; | ||
| 9 | +import org.mapstruct.factory.Mappers; | ||
| 10 | + | ||
| 11 | +@Mapper | ||
| 12 | +public interface OAuth2TokenConvert { | ||
| 13 | + | ||
| 14 | + OAuth2TokenConvert INSTANCE = Mappers.getMapper(OAuth2TokenConvert.class); | ||
| 15 | + | ||
| 16 | + OAuth2AccessTokenCheckRespDTO convert(OAuth2AccessTokenDO bean); | ||
| 17 | + | ||
| 18 | + PageResult<OAuth2AccessTokenRespVO> convert(PageResult<OAuth2AccessTokenDO> page); | ||
| 19 | + | ||
| 20 | + OAuth2AccessTokenRespDTO convert2(OAuth2AccessTokenDO bean); | ||
| 21 | + | ||
| 22 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/auth/UserSessionConvert.java deleted
| 1 | -package cn.iocoder.yudao.module.system.convert.auth; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageItemRespVO; | ||
| 4 | -import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO; | ||
| 5 | -import org.mapstruct.Mapper; | ||
| 6 | -import org.mapstruct.factory.Mappers; | ||
| 7 | - | ||
| 8 | -@Mapper | ||
| 9 | -public interface UserSessionConvert { | ||
| 10 | - | ||
| 11 | - UserSessionConvert INSTANCE = Mappers.getMapper(UserSessionConvert.class); | ||
| 12 | - | ||
| 13 | - UserSessionPageItemRespVO convert(UserSessionDO session); | ||
| 14 | - | ||
| 15 | -} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2AccessTokenDO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.dal.dataobject.auth; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||
| 4 | +import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; | ||
| 5 | +import com.baomidou.mybatisplus.annotation.TableId; | ||
| 6 | +import com.baomidou.mybatisplus.annotation.TableName; | ||
| 7 | +import lombok.Data; | ||
| 8 | +import lombok.EqualsAndHashCode; | ||
| 9 | +import lombok.experimental.Accessors; | ||
| 10 | + | ||
| 11 | +import java.util.Date; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * OAuth2 访问令牌 DO | ||
| 15 | + * | ||
| 16 | + * 如下字段,暂时未使用,暂时不支持: | ||
| 17 | + * user_name、authentication(用户信息) | ||
| 18 | + * | ||
| 19 | + * @author 芋道源码 | ||
| 20 | + */ | ||
| 21 | +@TableName("system_oauth2_access_token") | ||
| 22 | +@Data | ||
| 23 | +@EqualsAndHashCode(callSuper = true) | ||
| 24 | +@Accessors(chain = true) | ||
| 25 | +public class OAuth2AccessTokenDO extends TenantBaseDO { | ||
| 26 | + | ||
| 27 | + /** | ||
| 28 | + * 编号,数据库递增 | ||
| 29 | + */ | ||
| 30 | + @TableId | ||
| 31 | + private Long id; | ||
| 32 | + /** | ||
| 33 | + * 访问令牌 | ||
| 34 | + */ | ||
| 35 | + private String accessToken; | ||
| 36 | + /** | ||
| 37 | + * 刷新令牌 | ||
| 38 | + */ | ||
| 39 | + private String refreshToken; | ||
| 40 | + /** | ||
| 41 | + * 用户编号 | ||
| 42 | + */ | ||
| 43 | + private Long userId; | ||
| 44 | + /** | ||
| 45 | + * 用户类型 | ||
| 46 | + * | ||
| 47 | + * 枚举 {@link UserTypeEnum} | ||
| 48 | + */ | ||
| 49 | + private Integer userType; | ||
| 50 | + /** | ||
| 51 | + * 客户端编号 | ||
| 52 | + * | ||
| 53 | + * 关联 {@link OAuth2ClientDO#getId()} | ||
| 54 | + */ | ||
| 55 | + private Long clientId; | ||
| 56 | + /** | ||
| 57 | + * 过期时间 | ||
| 58 | + */ | ||
| 59 | + private Date expiresTime; | ||
| 60 | + | ||
| 61 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2ClientDO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.dal.dataobject.auth; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | ||
| 5 | +import com.baomidou.mybatisplus.annotation.IdType; | ||
| 6 | +import com.baomidou.mybatisplus.annotation.TableField; | ||
| 7 | +import com.baomidou.mybatisplus.annotation.TableId; | ||
| 8 | +import com.baomidou.mybatisplus.annotation.TableName; | ||
| 9 | +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; | ||
| 10 | +import lombok.Data; | ||
| 11 | +import lombok.EqualsAndHashCode; | ||
| 12 | +import lombok.experimental.Accessors; | ||
| 13 | + | ||
| 14 | +import java.util.List; | ||
| 15 | + | ||
| 16 | +/** | ||
| 17 | + * OAuth2 客户端 DO | ||
| 18 | + * | ||
| 19 | + * 如下字段,考虑到使用相对不是很高频,主要是一些开关,暂时不支持: | ||
| 20 | + * authorized_grant_types、authorities、additional_information、autoapprove、resource_ids、scope | ||
| 21 | + * | ||
| 22 | + * @author 芋道源码 | ||
| 23 | + */ | ||
| 24 | +@TableName(value = "system_oauth2_client", autoResultMap = true) | ||
| 25 | +@Data | ||
| 26 | +@EqualsAndHashCode(callSuper = true) | ||
| 27 | +@Accessors(chain = true) | ||
| 28 | +public class OAuth2ClientDO extends BaseDO { | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * 客户端编号 | ||
| 32 | + * | ||
| 33 | + * 由于 SQL Server 在存储 String 主键有点问题,所以暂时使用 Long 类型 | ||
| 34 | + */ | ||
| 35 | + @TableId(type = IdType.INPUT) | ||
| 36 | + private Long id; | ||
| 37 | + /** | ||
| 38 | + * 客户端密钥 | ||
| 39 | + */ | ||
| 40 | + private String secret; | ||
| 41 | + /** | ||
| 42 | + * 应用名 | ||
| 43 | + */ | ||
| 44 | + private String name; | ||
| 45 | + /** | ||
| 46 | + * 应用图标 | ||
| 47 | + */ | ||
| 48 | + private String logo; | ||
| 49 | + /** | ||
| 50 | + * 应用描述 | ||
| 51 | + */ | ||
| 52 | + private String description; | ||
| 53 | + /** | ||
| 54 | + * 状态 | ||
| 55 | + * | ||
| 56 | + * 枚举 {@link CommonStatusEnum} | ||
| 57 | + */ | ||
| 58 | + private Integer status; | ||
| 59 | + /** | ||
| 60 | + * 访问令牌的有效期 | ||
| 61 | + */ | ||
| 62 | + private Integer accessTokenValiditySeconds; | ||
| 63 | + /** | ||
| 64 | + * 刷新令牌的有效期 | ||
| 65 | + */ | ||
| 66 | + private Integer refreshTokenValiditySeconds; | ||
| 67 | + /** | ||
| 68 | + * 可重定向的 URI 地址 | ||
| 69 | + */ | ||
| 70 | + @TableField(typeHandler = JacksonTypeHandler.class) | ||
| 71 | + private List<String> redirectUris; | ||
| 72 | + | ||
| 73 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/UserSessionDO.java renamed to yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2CodeDO.java
| @@ -2,43 +2,34 @@ package cn.iocoder.yudao.module.system.dal.dataobject.auth; | @@ -2,43 +2,34 @@ package cn.iocoder.yudao.module.system.dal.dataobject.auth; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | 3 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 5 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 6 | -import com.baomidou.mybatisplus.annotation.*; | ||
| 7 | -import lombok.Builder; | 5 | +import com.baomidou.mybatisplus.annotation.TableName; |
| 8 | import lombok.Data; | 6 | import lombok.Data; |
| 9 | import lombok.EqualsAndHashCode; | 7 | import lombok.EqualsAndHashCode; |
| 8 | +import lombok.experimental.Accessors; | ||
| 10 | 9 | ||
| 11 | import java.util.Date; | 10 | import java.util.Date; |
| 12 | 11 | ||
| 13 | /** | 12 | /** |
| 14 | - * 在线用户表 | ||
| 15 | - * | ||
| 16 | - * 我们已经将 {@link LoginUser} 缓存在 Redis 当中。 | ||
| 17 | - * 这里额外存储在线用户到 MySQL 中,目的是为了方便管理界面可以灵活查询。 | ||
| 18 | - * 同时,通过定时轮询 UserSessionDO 表,可以主动删除 Redis 的缓存,因为 Redis 的过期删除是延迟的。 | 13 | + * OAuth2 授权码 DO |
| 19 | * | 14 | * |
| 20 | * @author 芋道源码 | 15 | * @author 芋道源码 |
| 21 | */ | 16 | */ |
| 22 | -@TableName(value = "system_user_session") | ||
| 23 | -@KeySequence(value = "system_user_session_seq") | 17 | +@TableName("system_oauth2_code") |
| 24 | @Data | 18 | @Data |
| 25 | -@Builder | ||
| 26 | @EqualsAndHashCode(callSuper = true) | 19 | @EqualsAndHashCode(callSuper = true) |
| 27 | -public class UserSessionDO extends BaseDO { | 20 | +@Accessors(chain = true) |
| 21 | +public class OAuth2CodeDO extends BaseDO { | ||
| 28 | 22 | ||
| 29 | /** | 23 | /** |
| 30 | - * 会话编号 | 24 | + * 编号,数据库递增 |
| 31 | */ | 25 | */ |
| 32 | private Long id; | 26 | private Long id; |
| 33 | /** | 27 | /** |
| 34 | - * 令牌 | 28 | + * 授权码 |
| 35 | */ | 29 | */ |
| 36 | - private String token; | ||
| 37 | - | 30 | + private String code; |
| 38 | /** | 31 | /** |
| 39 | * 用户编号 | 32 | * 用户编号 |
| 40 | - * | ||
| 41 | - * 关联 AdminUserDO.id 或者 MemberUserDO.id | ||
| 42 | */ | 33 | */ |
| 43 | private Long userId; | 34 | private Long userId; |
| 44 | /** | 35 | /** |
| @@ -47,25 +38,25 @@ public class UserSessionDO extends BaseDO { | @@ -47,25 +38,25 @@ public class UserSessionDO extends BaseDO { | ||
| 47 | * 枚举 {@link UserTypeEnum} | 38 | * 枚举 {@link UserTypeEnum} |
| 48 | */ | 39 | */ |
| 49 | private Integer userType; | 40 | private Integer userType; |
| 50 | - | ||
| 51 | /** | 41 | /** |
| 52 | - * 用户账号 | 42 | + * 客户端编号 |
| 53 | * | 43 | * |
| 54 | - * 冗余,因为账号可以变更 | 44 | + * 关联 {@link OAuth2ClientDO#getId()} |
| 55 | */ | 45 | */ |
| 56 | - private String username; | ||
| 57 | - | 46 | + private Long clientId; |
| 58 | /** | 47 | /** |
| 59 | - * 用户 IP | 48 | + * 刷新令牌 |
| 49 | + * | ||
| 50 | + * 关联 {@link OAuth2RefreshTokenDO#getRefreshToken()} | ||
| 60 | */ | 51 | */ |
| 61 | - private String userIp; | 52 | + private String refreshToken; |
| 62 | /** | 53 | /** |
| 63 | - * 浏览器 UA | 54 | + * 过期时间 |
| 64 | */ | 55 | */ |
| 65 | - private String userAgent; | 56 | + private Date expiresTime; |
| 66 | /** | 57 | /** |
| 67 | - * 会话超时时间 | 58 | + * 创建 IP |
| 68 | */ | 59 | */ |
| 69 | - private Date sessionTimeout; | 60 | + private String createIp; |
| 70 | 61 | ||
| 71 | } | 62 | } |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/auth/OAuth2RefreshTokenDO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.dal.dataobject.auth; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | ||
| 5 | +import com.baomidou.mybatisplus.annotation.TableName; | ||
| 6 | +import lombok.Data; | ||
| 7 | +import lombok.EqualsAndHashCode; | ||
| 8 | +import lombok.experimental.Accessors; | ||
| 9 | + | ||
| 10 | +import java.util.Date; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * OAuth2 刷新令牌 | ||
| 14 | + * | ||
| 15 | + * @author 芋道源码 | ||
| 16 | + */ | ||
| 17 | +@TableName("system_oauth2_refresh_token") | ||
| 18 | +@Data | ||
| 19 | +@EqualsAndHashCode(callSuper = true) | ||
| 20 | +@Accessors(chain = true) | ||
| 21 | +public class OAuth2RefreshTokenDO extends BaseDO { | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * 编号,数据库字典 | ||
| 25 | + */ | ||
| 26 | + private Long id; | ||
| 27 | + /** | ||
| 28 | + * 刷新令牌 | ||
| 29 | + */ | ||
| 30 | + private String refreshToken; | ||
| 31 | + /** | ||
| 32 | + * 用户编号 | ||
| 33 | + */ | ||
| 34 | + private Long userId; | ||
| 35 | + /** | ||
| 36 | + * 用户类型 | ||
| 37 | + * | ||
| 38 | + * 枚举 {@link UserTypeEnum} | ||
| 39 | + */ | ||
| 40 | + private Integer userType; | ||
| 41 | + /** | ||
| 42 | + * 客户端编号 | ||
| 43 | + * | ||
| 44 | + * 关联 {@link OAuth2ClientDO#getId()} | ||
| 45 | + */ | ||
| 46 | + private Long clientId; | ||
| 47 | + /** | ||
| 48 | + * 过期时间 | ||
| 49 | + */ | ||
| 50 | + private Date expiresTime; | ||
| 51 | + | ||
| 52 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2AccessTokenMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.dal.mysql.auth; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 5 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | ||
| 6 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO; | ||
| 7 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; | ||
| 8 | +import org.apache.ibatis.annotations.Mapper; | ||
| 9 | + | ||
| 10 | +import java.util.Date; | ||
| 11 | +import java.util.List; | ||
| 12 | + | ||
| 13 | +@Mapper | ||
| 14 | +public interface OAuth2AccessTokenMapper extends BaseMapperX<OAuth2AccessTokenDO> { | ||
| 15 | + | ||
| 16 | + default OAuth2AccessTokenDO selectByAccessToken(String accessToken) { | ||
| 17 | + return selectOne(OAuth2AccessTokenDO::getAccessToken, accessToken); | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + default List<OAuth2AccessTokenDO> selectListByRefreshToken(String refreshToken) { | ||
| 21 | + return selectList(OAuth2AccessTokenDO::getRefreshToken, refreshToken); | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + default PageResult<OAuth2AccessTokenDO> selectPage(OAuth2AccessTokenPageReqVO reqVO) { | ||
| 25 | + return selectPage(reqVO, new LambdaQueryWrapperX<OAuth2AccessTokenDO>() | ||
| 26 | + .eqIfPresent(OAuth2AccessTokenDO::getUserId, reqVO.getUserId()) | ||
| 27 | + .eqIfPresent(OAuth2AccessTokenDO::getUserType, reqVO.getUserType()) | ||
| 28 | + .eqIfPresent(OAuth2AccessTokenDO::getClientId, reqVO.getClientId()) | ||
| 29 | + .gt(OAuth2AccessTokenDO::getExpiresTime, new Date()) | ||
| 30 | + .orderByDesc(OAuth2AccessTokenDO::getId)); | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2ClientMapper.java
0 → 100755
| 1 | +package cn.iocoder.yudao.module.system.dal.mysql.auth; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 5 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | ||
| 6 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientPageReqVO; | ||
| 7 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; | ||
| 8 | +import org.apache.ibatis.annotations.Mapper; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * OAuth2 客户端 Mapper | ||
| 12 | + * | ||
| 13 | + * @author 芋道源码 | ||
| 14 | + */ | ||
| 15 | +@Mapper | ||
| 16 | +public interface OAuth2ClientMapper extends BaseMapperX<OAuth2ClientDO> { | ||
| 17 | + | ||
| 18 | + default PageResult<OAuth2ClientDO> selectPage(OAuth2ClientPageReqVO reqVO) { | ||
| 19 | + return selectPage(reqVO, new LambdaQueryWrapperX<OAuth2ClientDO>() | ||
| 20 | + .likeIfPresent(OAuth2ClientDO::getName, reqVO.getName()) | ||
| 21 | + .eqIfPresent(OAuth2ClientDO::getStatus, reqVO.getStatus()) | ||
| 22 | + .orderByDesc(OAuth2ClientDO::getId)); | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/OAuth2RefreshTokenMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.dal.mysql.auth; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | ||
| 5 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO; | ||
| 6 | +import org.apache.ibatis.annotations.Mapper; | ||
| 7 | + | ||
| 8 | +@Mapper | ||
| 9 | +public interface OAuth2RefreshTokenMapper extends BaseMapperX<OAuth2RefreshTokenDO> { | ||
| 10 | + | ||
| 11 | + default int deleteByRefreshToken(String refreshToken) { | ||
| 12 | + return delete(new LambdaQueryWrapperX<OAuth2RefreshTokenDO>() | ||
| 13 | + .eq(OAuth2RefreshTokenDO::getRefreshToken, refreshToken)); | ||
| 14 | + } | ||
| 15 | + | ||
| 16 | + default OAuth2RefreshTokenDO selectByRefreshToken(String refreshToken) { | ||
| 17 | + return selectOne(OAuth2RefreshTokenDO::getRefreshToken, refreshToken); | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/auth/UserSessionMapper.java deleted
| 1 | -package cn.iocoder.yudao.module.system.dal.mysql.auth; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 5 | -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | ||
| 6 | -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO; | ||
| 7 | -import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO; | ||
| 8 | -import org.apache.ibatis.annotations.Mapper; | ||
| 9 | - | ||
| 10 | -import java.util.Collection; | ||
| 11 | -import java.util.Date; | ||
| 12 | -import java.util.List; | ||
| 13 | - | ||
| 14 | -@Mapper | ||
| 15 | -public interface UserSessionMapper extends BaseMapperX<UserSessionDO> { | ||
| 16 | - | ||
| 17 | - default PageResult<UserSessionDO> selectPage(UserSessionPageReqVO reqVO, Collection<Long> userIds) { | ||
| 18 | - return selectPage(reqVO, new LambdaQueryWrapperX<UserSessionDO>() | ||
| 19 | - .inIfPresent(UserSessionDO::getUserId, userIds) | ||
| 20 | - .likeIfPresent(UserSessionDO::getUserIp, reqVO.getUserIp())); | ||
| 21 | - } | ||
| 22 | - | ||
| 23 | - default List<UserSessionDO> selectListBySessionTimoutLt() { | ||
| 24 | - return selectList(new LambdaQueryWrapperX<UserSessionDO>() | ||
| 25 | - .lt(UserSessionDO::getSessionTimeout, new Date())); | ||
| 26 | - } | ||
| 27 | - | ||
| 28 | - default void updateByToken(String token, UserSessionDO updateObj) { | ||
| 29 | - update(updateObj, new LambdaQueryWrapperX<UserSessionDO>() | ||
| 30 | - .eq(UserSessionDO::getToken, token)); | ||
| 31 | - } | ||
| 32 | - | ||
| 33 | - default void deleteByToken(String token) { | ||
| 34 | - delete(new LambdaQueryWrapperX<UserSessionDO>().eq(UserSessionDO::getToken, token)); | ||
| 35 | - } | ||
| 36 | - | ||
| 37 | -} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/RedisKeyConstants.java
| 1 | package cn.iocoder.yudao.module.system.dal.redis; | 1 | package cn.iocoder.yudao.module.system.dal.redis; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; | 3 | import cn.iocoder.yudao.framework.redis.core.RedisKeyDefine; |
| 4 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | 4 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; |
| 5 | 5 | ||
| 6 | import java.time.Duration; | 6 | import java.time.Duration; |
| 7 | 7 | ||
| @@ -18,9 +18,9 @@ public interface RedisKeyConstants { | @@ -18,9 +18,9 @@ public interface RedisKeyConstants { | ||
| 18 | "captcha_code:%s", // 参数为 uuid | 18 | "captcha_code:%s", // 参数为 uuid |
| 19 | STRING, String.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); | 19 | STRING, String.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); |
| 20 | 20 | ||
| 21 | - RedisKeyDefine LOGIN_USER = new RedisKeyDefine("登录用户的缓存", | ||
| 22 | - "login_user:%s", // 参数为 token 令牌 | ||
| 23 | - STRING, LoginUser.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); | 21 | + RedisKeyDefine OAUTH2_ACCESS_TOKEN = new RedisKeyDefine("访问令牌的缓存", |
| 22 | + "oauth2_access_token:%s", // 参数为访问令牌 token | ||
| 23 | + STRING, OAuth2AccessTokenDO.class, RedisKeyDefine.TimeoutTypeEnum.DYNAMIC); | ||
| 24 | 24 | ||
| 25 | RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交登陆的 state", // 注意,它是被 JustAuth 的 justauth.type.prefix 使用到 | 25 | RedisKeyDefine SOCIAL_AUTH_STATE = new RedisKeyDefine("社交登陆的 state", // 注意,它是被 JustAuth 的 justauth.type.prefix 使用到 |
| 26 | "social_auth_state:%s", // 参数为 state | 26 | "social_auth_state:%s", // 参数为 state |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/auth/LoginUserRedisDAO.java deleted
| 1 | -package cn.iocoder.yudao.module.system.dal.redis.auth; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||
| 4 | -import cn.iocoder.yudao.framework.security.config.SecurityProperties; | ||
| 5 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 6 | -import org.springframework.data.redis.core.StringRedisTemplate; | ||
| 7 | -import org.springframework.stereotype.Repository; | ||
| 8 | - | ||
| 9 | -import javax.annotation.Resource; | ||
| 10 | - | ||
| 11 | -import static cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants.LOGIN_USER; | ||
| 12 | - | ||
| 13 | -/** | ||
| 14 | - * {@link LoginUser} 的 RedisDAO | ||
| 15 | - * | ||
| 16 | - * @author 芋道源码 | ||
| 17 | - */ | ||
| 18 | -@Repository | ||
| 19 | -public class LoginUserRedisDAO { | ||
| 20 | - | ||
| 21 | - @Resource | ||
| 22 | - private StringRedisTemplate stringRedisTemplate; | ||
| 23 | - | ||
| 24 | - @Resource | ||
| 25 | - private SecurityProperties securityProperties; | ||
| 26 | - | ||
| 27 | - public LoginUser get(String token) { | ||
| 28 | - String redisKey = formatKey(token); | ||
| 29 | - return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), LoginUser.class); | ||
| 30 | - } | ||
| 31 | - | ||
| 32 | - public Boolean exists(String token) { | ||
| 33 | - String redisKey = formatKey(token); | ||
| 34 | - return stringRedisTemplate.hasKey(redisKey); | ||
| 35 | - } | ||
| 36 | - | ||
| 37 | - public void set(String token, LoginUser loginUser) { | ||
| 38 | - String redisKey = formatKey(token); | ||
| 39 | - stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(loginUser), | ||
| 40 | - securityProperties.getSessionTimeout()); | ||
| 41 | - } | ||
| 42 | - | ||
| 43 | - public void delete(String token) { | ||
| 44 | - String redisKey = formatKey(token); | ||
| 45 | - stringRedisTemplate.delete(redisKey); | ||
| 46 | - } | ||
| 47 | - | ||
| 48 | - private static String formatKey(String token) { | ||
| 49 | - return LOGIN_USER.formatKey(token); | ||
| 50 | - } | ||
| 51 | - | ||
| 52 | -} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/redis/auth/OAuth2AccessTokenRedisDAO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.dal.redis.auth; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | ||
| 4 | +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||
| 5 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; | ||
| 6 | +import org.springframework.data.redis.core.StringRedisTemplate; | ||
| 7 | +import org.springframework.stereotype.Repository; | ||
| 8 | + | ||
| 9 | +import javax.annotation.Resource; | ||
| 10 | +import java.util.Collection; | ||
| 11 | +import java.util.List; | ||
| 12 | +import java.util.concurrent.TimeUnit; | ||
| 13 | + | ||
| 14 | +import static cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN; | ||
| 15 | + | ||
| 16 | +/** | ||
| 17 | + * {@link OAuth2AccessTokenDO} 的 RedisDAO | ||
| 18 | + * | ||
| 19 | + * @author 芋道源码 | ||
| 20 | + */ | ||
| 21 | +@Repository | ||
| 22 | +public class OAuth2AccessTokenRedisDAO { | ||
| 23 | + | ||
| 24 | + @Resource | ||
| 25 | + private StringRedisTemplate stringRedisTemplate; | ||
| 26 | + | ||
| 27 | + public OAuth2AccessTokenDO get(String accessToken) { | ||
| 28 | + String redisKey = formatKey(accessToken); | ||
| 29 | + return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), OAuth2AccessTokenDO.class); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + public void set(OAuth2AccessTokenDO accessTokenDO) { | ||
| 33 | + String redisKey = formatKey(accessTokenDO.getAccessToken()); | ||
| 34 | + // 清理多余字段,避免缓存 | ||
| 35 | + accessTokenDO.setUpdater(null).setUpdateTime(null).setCreateTime(null).setCreator(null).setDeleted(null); | ||
| 36 | + stringRedisTemplate.opsForValue().set(redisKey, JsonUtils.toJsonString(accessTokenDO), | ||
| 37 | + accessTokenDO.getExpiresTime().getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + public void delete(String accessToken) { | ||
| 41 | + String redisKey = formatKey(accessToken); | ||
| 42 | + stringRedisTemplate.delete(redisKey); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + public void deleteList(Collection<String> accessTokens) { | ||
| 46 | + List<String> redisKeys = CollectionUtils.convertList(accessTokens, OAuth2AccessTokenRedisDAO::formatKey); | ||
| 47 | + stringRedisTemplate.delete(redisKeys); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + private static String formatKey(String accessToken) { | ||
| 51 | + return String.format(OAUTH2_ACCESS_TOKEN.getKeyTemplate(), accessToken); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/security/config/SecurityConfiguration.java
| @@ -20,6 +20,8 @@ public class SecurityConfiguration { | @@ -20,6 +20,8 @@ public class SecurityConfiguration { | ||
| 20 | public void customize(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) { | 20 | public void customize(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) { |
| 21 | // 登录的接口 | 21 | // 登录的接口 |
| 22 | registry.antMatchers(buildAdminApi("/system/auth/login")).permitAll(); | 22 | registry.antMatchers(buildAdminApi("/system/auth/login")).permitAll(); |
| 23 | + registry.antMatchers(buildAdminApi("/system/auth/logout")).permitAll(); | ||
| 24 | + registry.antMatchers(buildAdminApi("/system/auth/refresh-token")).permitAll(); | ||
| 23 | // 社交登陆的接口 | 25 | // 社交登陆的接口 |
| 24 | registry.antMatchers(buildAdminApi("/system/auth/social-auth-redirect")).permitAll(); | 26 | registry.antMatchers(buildAdminApi("/system/auth/social-auth-redirect")).permitAll(); |
| 25 | registry.antMatchers(buildAdminApi("/system/auth/social-quick-login")).permitAll(); | 27 | registry.antMatchers(buildAdminApi("/system/auth/social-quick-login")).permitAll(); |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/job/auth/UserSessionTimeoutJob.java deleted
| 1 | -package cn.iocoder.yudao.module.system.job.auth; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; | ||
| 4 | -import cn.iocoder.yudao.module.system.service.auth.UserSessionService; | ||
| 5 | -import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; | ||
| 6 | -import lombok.extern.slf4j.Slf4j; | ||
| 7 | -import org.springframework.stereotype.Component; | ||
| 8 | - | ||
| 9 | -import javax.annotation.Resource; | ||
| 10 | - | ||
| 11 | -/** | ||
| 12 | - * 用户 Session 超时 Job | ||
| 13 | - * | ||
| 14 | - * @author 願 | ||
| 15 | - */ | ||
| 16 | -@Component | ||
| 17 | -@TenantJob | ||
| 18 | -@Slf4j | ||
| 19 | -public class UserSessionTimeoutJob implements JobHandler { | ||
| 20 | - | ||
| 21 | - @Resource | ||
| 22 | - private UserSessionService userSessionService; | ||
| 23 | - | ||
| 24 | - @Override | ||
| 25 | - public String execute(String param) throws Exception { | ||
| 26 | - // 执行过期 | ||
| 27 | - Long timeoutCount = userSessionService.deleteTimeoutSession(); | ||
| 28 | - // 返回结果,记录每次的超时数量 | ||
| 29 | - return String.format("移除在线会话数量为 %s 个", timeoutCount); | ||
| 30 | - } | ||
| 31 | - | ||
| 32 | -} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthService.java
| 1 | package cn.iocoder.yudao.module.system.service.auth; | 1 | package cn.iocoder.yudao.module.system.service.auth; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; | 3 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; |
| 4 | -import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService; | ||
| 5 | 4 | ||
| 6 | import javax.validation.Valid; | 5 | import javax.validation.Valid; |
| 7 | 6 | ||
| 8 | /** | 7 | /** |
| 9 | * 管理后台的认证 Service 接口 | 8 | * 管理后台的认证 Service 接口 |
| 10 | * | 9 | * |
| 11 | - * 提供用户的账号密码登录、token 的校验等认证相关的功能 | 10 | + * 提供用户的登录、登出的能力 |
| 12 | * | 11 | * |
| 13 | * @author 芋道源码 | 12 | * @author 芋道源码 |
| 14 | */ | 13 | */ |
| 15 | -public interface AdminAuthService extends SecurityAuthFrameworkService { | 14 | +public interface AdminAuthService { |
| 16 | 15 | ||
| 17 | /** | 16 | /** |
| 18 | * 账号登录 | 17 | * 账号登录 |
| 19 | * | 18 | * |
| 20 | * @param reqVO 登录信息 | 19 | * @param reqVO 登录信息 |
| 21 | - * @param userIp 用户 IP | ||
| 22 | - * @param userAgent 用户 UA | ||
| 23 | - * @return 身份令牌,使用 JWT 方式 | 20 | + * @return 登录结果 |
| 24 | */ | 21 | */ |
| 25 | - String login(@Valid AuthLoginReqVO reqVO, String userIp, String userAgent); | 22 | + AuthLoginRespVO login(@Valid AuthLoginReqVO reqVO); |
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 基于 token 退出登录 | ||
| 26 | + * | ||
| 27 | + * @param token token | ||
| 28 | + * @param logType 登出类型 | ||
| 29 | + */ | ||
| 30 | + void logout(String token, Integer logType); | ||
| 26 | 31 | ||
| 27 | /** | 32 | /** |
| 28 | * 短信验证码发送 | 33 | * 短信验证码发送 |
| @@ -35,30 +40,32 @@ public interface AdminAuthService extends SecurityAuthFrameworkService { | @@ -35,30 +40,32 @@ public interface AdminAuthService extends SecurityAuthFrameworkService { | ||
| 35 | * 短信登录 | 40 | * 短信登录 |
| 36 | * | 41 | * |
| 37 | * @param reqVO 登录信息 | 42 | * @param reqVO 登录信息 |
| 38 | - * @param userIp 用户 IP | ||
| 39 | - * @param userAgent 用户 UA | ||
| 40 | - * @return 身份令牌,使用 JWT 方式 | 43 | + * @return 登录结果 |
| 41 | */ | 44 | */ |
| 42 | - String smsLogin(AuthSmsLoginReqVO reqVO, String userIp, String userAgent) ; | 45 | + AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO) ; |
| 43 | 46 | ||
| 44 | /** | 47 | /** |
| 45 | * 社交快捷登录,使用 code 授权码 | 48 | * 社交快捷登录,使用 code 授权码 |
| 46 | * | 49 | * |
| 47 | * @param reqVO 登录信息 | 50 | * @param reqVO 登录信息 |
| 48 | - * @param userIp 用户 IP | ||
| 49 | - * @param userAgent 用户 UA | ||
| 50 | - * @return 身份令牌,使用 JWT 方式 | 51 | + * @return 登录结果 |
| 51 | */ | 52 | */ |
| 52 | - String socialQuickLogin(@Valid AuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent); | 53 | + AuthLoginRespVO socialQuickLogin(@Valid AuthSocialQuickLoginReqVO reqVO); |
| 53 | 54 | ||
| 54 | /** | 55 | /** |
| 55 | * 社交绑定登录,使用 code 授权码 + 账号密码 | 56 | * 社交绑定登录,使用 code 授权码 + 账号密码 |
| 56 | * | 57 | * |
| 57 | * @param reqVO 登录信息 | 58 | * @param reqVO 登录信息 |
| 58 | - * @param userIp 用户 IP | ||
| 59 | - * @param userAgent 用户 UA | ||
| 60 | - * @return 身份令牌,使用 JWT 方式 | 59 | + * @return 登录结果 |
| 60 | + */ | ||
| 61 | + AuthLoginRespVO socialBindLogin(@Valid AuthSocialBindLoginReqVO reqVO); | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 刷新访问令牌 | ||
| 65 | + * | ||
| 66 | + * @param refreshToken 刷新令牌 | ||
| 67 | + * @return 登录结果 | ||
| 61 | */ | 68 | */ |
| 62 | - String socialBindLogin(@Valid AuthSocialBindLoginReqVO reqVO, String userIp, String userAgent); | 69 | + AuthLoginRespVO refreshToken(String refreshToken); |
| 63 | 70 | ||
| 64 | } | 71 | } |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminAuthServiceImpl.java
| 1 | package cn.iocoder.yudao.module.system.service.auth; | 1 | package cn.iocoder.yudao.module.system.service.auth; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.util.ObjectUtil; | ||
| 3 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 4 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 4 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | 5 | import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 5 | import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; | 6 | import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; |
| 6 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; | 7 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; |
| 7 | import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; | 8 | import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; |
| 8 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 9 | -import cn.iocoder.yudao.framework.security.core.authentication.MultiUsernamePasswordAuthenticationToken; | ||
| 10 | import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; | 9 | import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; |
| 11 | import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; | 10 | import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; |
| 12 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; | 11 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.*; |
| 13 | import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; | 12 | import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; |
| 13 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; | ||
| 14 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | 14 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; |
| 15 | +import cn.iocoder.yudao.module.system.enums.auth.OAuth2ClientIdEnum; | ||
| 15 | import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; | 16 | import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; |
| 16 | import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; | 17 | import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; |
| 17 | import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; | 18 | import cn.iocoder.yudao.module.system.enums.sms.SmsSceneEnum; |
| 18 | import cn.iocoder.yudao.module.system.service.common.CaptchaService; | 19 | import cn.iocoder.yudao.module.system.service.common.CaptchaService; |
| 19 | import cn.iocoder.yudao.module.system.service.logger.LoginLogService; | 20 | import cn.iocoder.yudao.module.system.service.logger.LoginLogService; |
| 20 | -import cn.iocoder.yudao.module.system.service.permission.PermissionService; | 21 | +import cn.iocoder.yudao.module.system.service.member.MemberService; |
| 21 | import cn.iocoder.yudao.module.system.service.social.SocialUserService; | 22 | import cn.iocoder.yudao.module.system.service.social.SocialUserService; |
| 22 | import cn.iocoder.yudao.module.system.service.user.AdminUserService; | 23 | import cn.iocoder.yudao.module.system.service.user.AdminUserService; |
| 24 | +import com.google.common.annotations.VisibleForTesting; | ||
| 23 | import lombok.extern.slf4j.Slf4j; | 25 | import lombok.extern.slf4j.Slf4j; |
| 24 | -import org.springframework.beans.factory.annotation.Autowired; | ||
| 25 | -import org.springframework.context.annotation.Lazy; | ||
| 26 | -import org.springframework.security.authentication.AuthenticationManager; | ||
| 27 | -import org.springframework.security.authentication.BadCredentialsException; | ||
| 28 | -import org.springframework.security.authentication.DisabledException; | ||
| 29 | -import org.springframework.security.core.Authentication; | ||
| 30 | -import org.springframework.security.core.AuthenticationException; | ||
| 31 | -import org.springframework.security.core.userdetails.UserDetails; | ||
| 32 | -import org.springframework.security.core.userdetails.UsernameNotFoundException; | ||
| 33 | import org.springframework.stereotype.Service; | 26 | import org.springframework.stereotype.Service; |
| 34 | -import org.springframework.util.Assert; | ||
| 35 | 27 | ||
| 36 | import javax.annotation.Resource; | 28 | import javax.annotation.Resource; |
| 37 | import javax.validation.Validator; | 29 | import javax.validation.Validator; |
| 38 | import java.util.Objects; | 30 | import java.util.Objects; |
| 39 | -import java.util.Set; | ||
| 40 | 31 | ||
| 41 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 32 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 42 | import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; | 33 | import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; |
| 43 | import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | 34 | import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; |
| 44 | -import static java.util.Collections.singleton; | ||
| 45 | 35 | ||
| 46 | /** | 36 | /** |
| 47 | * Auth Service 实现类 | 37 | * Auth Service 实现类 |
| @@ -53,22 +43,17 @@ import static java.util.Collections.singleton; | @@ -53,22 +43,17 @@ import static java.util.Collections.singleton; | ||
| 53 | public class AdminAuthServiceImpl implements AdminAuthService { | 43 | public class AdminAuthServiceImpl implements AdminAuthService { |
| 54 | 44 | ||
| 55 | @Resource | 45 | @Resource |
| 56 | - @Lazy // 延迟加载,因为存在相互依赖的问题 | ||
| 57 | - private AuthenticationManager authenticationManager; | ||
| 58 | - | ||
| 59 | - @Autowired | ||
| 60 | - @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") // UserService 存在重名 | ||
| 61 | private AdminUserService userService; | 46 | private AdminUserService userService; |
| 62 | @Resource | 47 | @Resource |
| 63 | - private PermissionService permissionService; | ||
| 64 | - @Resource | ||
| 65 | private CaptchaService captchaService; | 48 | private CaptchaService captchaService; |
| 66 | @Resource | 49 | @Resource |
| 67 | private LoginLogService loginLogService; | 50 | private LoginLogService loginLogService; |
| 68 | @Resource | 51 | @Resource |
| 69 | - private UserSessionService userSessionService; | 52 | + private OAuth2TokenService oauth2TokenService; |
| 70 | @Resource | 53 | @Resource |
| 71 | private SocialUserService socialUserService; | 54 | private SocialUserService socialUserService; |
| 55 | + @Resource | ||
| 56 | + private MemberService memberService; | ||
| 72 | 57 | ||
| 73 | @Resource | 58 | @Resource |
| 74 | private Validator validator; | 59 | private Validator validator; |
| @@ -77,39 +62,15 @@ public class AdminAuthServiceImpl implements AdminAuthService { | @@ -77,39 +62,15 @@ public class AdminAuthServiceImpl implements AdminAuthService { | ||
| 77 | private SmsCodeApi smsCodeApi; | 62 | private SmsCodeApi smsCodeApi; |
| 78 | 63 | ||
| 79 | @Override | 64 | @Override |
| 80 | - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { | ||
| 81 | - // 获取 username 对应的 AdminUserDO | ||
| 82 | - AdminUserDO user = userService.getUserByUsername(username); | ||
| 83 | - if (user == null) { | ||
| 84 | - throw new UsernameNotFoundException(username); | ||
| 85 | - } | ||
| 86 | - // 创建 LoginUser 对象 | ||
| 87 | - return buildLoginUser(user); | ||
| 88 | - } | ||
| 89 | - | ||
| 90 | - @Override | ||
| 91 | - public LoginUser mockLogin(Long userId) { | ||
| 92 | - // 获取用户编号对应的 AdminUserDO | ||
| 93 | - AdminUserDO user = userService.getUser(userId); | ||
| 94 | - if (user == null) { | ||
| 95 | - throw new UsernameNotFoundException(String.valueOf(userId)); | ||
| 96 | - } | ||
| 97 | - createLoginLog(user.getUsername(), LoginLogTypeEnum.LOGIN_MOCK, LoginResultEnum.SUCCESS); | ||
| 98 | - | ||
| 99 | - // 创建 LoginUser 对象 | ||
| 100 | - return buildLoginUser(user); | ||
| 101 | - } | ||
| 102 | - | ||
| 103 | - @Override | ||
| 104 | - public String login(AuthLoginReqVO reqVO, String userIp, String userAgent) { | 65 | + public AuthLoginRespVO login(AuthLoginReqVO reqVO) { |
| 105 | // 判断验证码是否正确 | 66 | // 判断验证码是否正确 |
| 106 | verifyCaptcha(reqVO); | 67 | verifyCaptcha(reqVO); |
| 107 | 68 | ||
| 108 | // 使用账号密码,进行登录 | 69 | // 使用账号密码,进行登录 |
| 109 | - LoginUser loginUser = login0(reqVO.getUsername(), reqVO.getPassword()); | 70 | + AdminUserDO user = login0(reqVO.getUsername(), reqVO.getPassword()); |
| 110 | 71 | ||
| 111 | - // 缓存登陆用户到 Redis 中,返回 Token 令牌 | ||
| 112 | - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_USERNAME, userIp, userAgent); | 72 | + // 创建 Token 令牌,记录登录日志 |
| 73 | + return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_USERNAME); | ||
| 113 | } | 74 | } |
| 114 | 75 | ||
| 115 | @Override | 76 | @Override |
| @@ -123,9 +84,9 @@ public class AdminAuthServiceImpl implements AdminAuthService { | @@ -123,9 +84,9 @@ public class AdminAuthServiceImpl implements AdminAuthService { | ||
| 123 | } | 84 | } |
| 124 | 85 | ||
| 125 | @Override | 86 | @Override |
| 126 | - public String smsLogin(AuthSmsLoginReqVO reqVO, String userIp, String userAgent) { | 87 | + public AuthLoginRespVO smsLogin(AuthSmsLoginReqVO reqVO) { |
| 127 | // 校验验证码 | 88 | // 校验验证码 |
| 128 | - smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), userIp)); | 89 | + smsCodeApi.useSmsCode(AuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.ADMIN_MEMBER_LOGIN.getScene(), getClientIP())); |
| 129 | 90 | ||
| 130 | // 获得用户信息 | 91 | // 获得用户信息 |
| 131 | AdminUserDO user = userService.getUserByMobile(reqVO.getMobile()); | 92 | AdminUserDO user = userService.getUserByMobile(reqVO.getMobile()); |
| @@ -133,14 +94,12 @@ public class AdminAuthServiceImpl implements AdminAuthService { | @@ -133,14 +94,12 @@ public class AdminAuthServiceImpl implements AdminAuthService { | ||
| 133 | throw exception(USER_NOT_EXISTS); | 94 | throw exception(USER_NOT_EXISTS); |
| 134 | } | 95 | } |
| 135 | 96 | ||
| 136 | - // 创建 LoginUser 对象 | ||
| 137 | - LoginUser loginUser = buildLoginUser(user); | ||
| 138 | - | ||
| 139 | // 缓存登陆用户到 Redis 中,返回 sessionId 编号 | 97 | // 缓存登陆用户到 Redis 中,返回 sessionId 编号 |
| 140 | - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_MOBILE, userIp, userAgent); | 98 | + return createTokenAfterLoginSuccess(user.getId(), reqVO.getMobile(), LoginLogTypeEnum.LOGIN_MOBILE); |
| 141 | } | 99 | } |
| 142 | 100 | ||
| 143 | - private void verifyCaptcha(AuthLoginReqVO reqVO) { | 101 | + @VisibleForTesting |
| 102 | + void verifyCaptcha(AuthLoginReqVO reqVO) { | ||
| 144 | // 如果验证码关闭,则不进行校验 | 103 | // 如果验证码关闭,则不进行校验 |
| 145 | if (!captchaService.isCaptchaEnable()) { | 104 | if (!captchaService.isCaptchaEnable()) { |
| 146 | return; | 105 | return; |
| @@ -152,53 +111,47 @@ public class AdminAuthServiceImpl implements AdminAuthService { | @@ -152,53 +111,47 @@ public class AdminAuthServiceImpl implements AdminAuthService { | ||
| 152 | String code = captchaService.getCaptchaCode(reqVO.getUuid()); | 111 | String code = captchaService.getCaptchaCode(reqVO.getUuid()); |
| 153 | if (code == null) { | 112 | if (code == null) { |
| 154 | // 创建登录失败日志(验证码不存在) | 113 | // 创建登录失败日志(验证码不存在) |
| 155 | - this.createLoginLog(reqVO.getUsername(), logTypeEnum, LoginResultEnum.CAPTCHA_NOT_FOUND); | 114 | + createLoginLog(null, reqVO.getUsername(), logTypeEnum, LoginResultEnum.CAPTCHA_NOT_FOUND); |
| 156 | throw exception(AUTH_LOGIN_CAPTCHA_NOT_FOUND); | 115 | throw exception(AUTH_LOGIN_CAPTCHA_NOT_FOUND); |
| 157 | } | 116 | } |
| 158 | // 验证码不正确 | 117 | // 验证码不正确 |
| 159 | if (!code.equals(reqVO.getCode())) { | 118 | if (!code.equals(reqVO.getCode())) { |
| 160 | // 创建登录失败日志(验证码不正确) | 119 | // 创建登录失败日志(验证码不正确) |
| 161 | - this.createLoginLog(reqVO.getUsername(), logTypeEnum, LoginResultEnum.CAPTCHA_CODE_ERROR); | 120 | + createLoginLog(null, reqVO.getUsername(), logTypeEnum, LoginResultEnum.CAPTCHA_CODE_ERROR); |
| 162 | throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR); | 121 | throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR); |
| 163 | } | 122 | } |
| 164 | // 正确,所以要删除下验证码 | 123 | // 正确,所以要删除下验证码 |
| 165 | captchaService.deleteCaptchaCode(reqVO.getUuid()); | 124 | captchaService.deleteCaptchaCode(reqVO.getUuid()); |
| 166 | } | 125 | } |
| 167 | 126 | ||
| 168 | - private LoginUser login0(String username, String password) { | 127 | + @VisibleForTesting |
| 128 | + AdminUserDO login0(String username, String password) { | ||
| 169 | final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME; | 129 | final LoginLogTypeEnum logTypeEnum = LoginLogTypeEnum.LOGIN_USERNAME; |
| 170 | - // 用户验证 | ||
| 171 | - Authentication authentication; | ||
| 172 | - try { | ||
| 173 | - // 调用 Spring Security 的 AuthenticationManager#authenticate(...) 方法,使用账号密码进行认证 | ||
| 174 | - // 在其内部,会调用到 loadUserByUsername 方法,获取 User 信息 | ||
| 175 | - authentication = authenticationManager.authenticate(new MultiUsernamePasswordAuthenticationToken( | ||
| 176 | - username, password, getUserType())); | ||
| 177 | - } catch (BadCredentialsException badCredentialsException) { | ||
| 178 | - this.createLoginLog(username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); | 130 | + // 校验账号是否存在 |
| 131 | + AdminUserDO user = userService.getUserByUsername(username); | ||
| 132 | + if (user == null) { | ||
| 133 | + createLoginLog(null, username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); | ||
| 179 | throw exception(AUTH_LOGIN_BAD_CREDENTIALS); | 134 | throw exception(AUTH_LOGIN_BAD_CREDENTIALS); |
| 180 | - } catch (DisabledException disabledException) { | ||
| 181 | - this.createLoginLog(username, logTypeEnum, LoginResultEnum.USER_DISABLED); | 135 | + } |
| 136 | + if (!userService.isPasswordMatch(password, user.getPassword())) { | ||
| 137 | + createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS); | ||
| 138 | + throw exception(AUTH_LOGIN_BAD_CREDENTIALS); | ||
| 139 | + } | ||
| 140 | + // 校验是否禁用 | ||
| 141 | + if (ObjectUtil.notEqual(user.getStatus(), CommonStatusEnum.ENABLE.getStatus())) { | ||
| 142 | + createLoginLog(user.getId(), username, logTypeEnum, LoginResultEnum.USER_DISABLED); | ||
| 182 | throw exception(AUTH_LOGIN_USER_DISABLED); | 143 | throw exception(AUTH_LOGIN_USER_DISABLED); |
| 183 | - } catch (AuthenticationException authenticationException) { | ||
| 184 | - log.error("[login0][username({}) 发生未知异常]", username, authenticationException); | ||
| 185 | - this.createLoginLog(username, logTypeEnum, LoginResultEnum.UNKNOWN_ERROR); | ||
| 186 | - throw exception(AUTH_LOGIN_FAIL_UNKNOWN); | ||
| 187 | } | 144 | } |
| 188 | - Assert.notNull(authentication.getPrincipal(), "Principal 不会为空"); | ||
| 189 | - return (LoginUser) authentication.getPrincipal(); | 145 | + return user; |
| 190 | } | 146 | } |
| 191 | 147 | ||
| 192 | - private void createLoginLog(String username, LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) { | ||
| 193 | - // 获得用户 | ||
| 194 | - AdminUserDO user = userService.getUserByUsername(username); | 148 | + private void createLoginLog(Long userId, String username, |
| 149 | + LoginLogTypeEnum logTypeEnum, LoginResultEnum loginResult) { | ||
| 195 | // 插入登录日志 | 150 | // 插入登录日志 |
| 196 | LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); | 151 | LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); |
| 197 | reqDTO.setLogType(logTypeEnum.getType()); | 152 | reqDTO.setLogType(logTypeEnum.getType()); |
| 198 | reqDTO.setTraceId(TracerUtils.getTraceId()); | 153 | reqDTO.setTraceId(TracerUtils.getTraceId()); |
| 199 | - if (user != null) { | ||
| 200 | - reqDTO.setUserId(user.getId()); | ||
| 201 | - } | 154 | + reqDTO.setUserId(userId); |
| 202 | reqDTO.setUserType(getUserType().getValue()); | 155 | reqDTO.setUserType(getUserType().getValue()); |
| 203 | reqDTO.setUsername(username); | 156 | reqDTO.setUsername(username); |
| 204 | reqDTO.setUserAgent(ServletUtils.getUserAgent()); | 157 | reqDTO.setUserAgent(ServletUtils.getUserAgent()); |
| @@ -206,23 +159,13 @@ public class AdminAuthServiceImpl implements AdminAuthService { | @@ -206,23 +159,13 @@ public class AdminAuthServiceImpl implements AdminAuthService { | ||
| 206 | reqDTO.setResult(loginResult.getResult()); | 159 | reqDTO.setResult(loginResult.getResult()); |
| 207 | loginLogService.createLoginLog(reqDTO); | 160 | loginLogService.createLoginLog(reqDTO); |
| 208 | // 更新最后登录时间 | 161 | // 更新最后登录时间 |
| 209 | - if (user != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { | ||
| 210 | - userService.updateUserLogin(user.getId(), ServletUtils.getClientIP()); | 162 | + if (userId != null && Objects.equals(LoginResultEnum.SUCCESS.getResult(), loginResult.getResult())) { |
| 163 | + userService.updateUserLogin(userId, ServletUtils.getClientIP()); | ||
| 211 | } | 164 | } |
| 212 | } | 165 | } |
| 213 | 166 | ||
| 214 | - /** | ||
| 215 | - * 获得 User 拥有的角色编号数组 | ||
| 216 | - * | ||
| 217 | - * @param userId 用户编号 | ||
| 218 | - * @return 角色编号数组 | ||
| 219 | - */ | ||
| 220 | - private Set<Long> getUserRoleIds(Long userId) { | ||
| 221 | - return permissionService.getUserRoleIds(userId, singleton(CommonStatusEnum.ENABLE.getStatus())); | ||
| 222 | - } | ||
| 223 | - | ||
| 224 | @Override | 167 | @Override |
| 225 | - public String socialQuickLogin(AuthSocialQuickLoginReqVO reqVO, String userIp, String userAgent) { | 168 | + public AuthLoginRespVO socialQuickLogin(AuthSocialQuickLoginReqVO reqVO) { |
| 226 | // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 | 169 | // 使用 code 授权码,进行登录。然后,获得到绑定的用户编号 |
| 227 | Long userId = socialUserService.getBindUserId(UserTypeEnum.ADMIN.getValue(), reqVO.getType(), | 170 | Long userId = socialUserService.getBindUserId(UserTypeEnum.ADMIN.getValue(), reqVO.getType(), |
| 228 | reqVO.getCode(), reqVO.getState()); | 171 | reqVO.getCode(), reqVO.getState()); |
| @@ -230,105 +173,82 @@ public class AdminAuthServiceImpl implements AdminAuthService { | @@ -230,105 +173,82 @@ public class AdminAuthServiceImpl implements AdminAuthService { | ||
| 230 | throw exception(AUTH_THIRD_LOGIN_NOT_BIND); | 173 | throw exception(AUTH_THIRD_LOGIN_NOT_BIND); |
| 231 | } | 174 | } |
| 232 | 175 | ||
| 233 | - // 自动登录 | 176 | + // 获得用户 |
| 234 | AdminUserDO user = userService.getUser(userId); | 177 | AdminUserDO user = userService.getUser(userId); |
| 235 | if (user == null) { | 178 | if (user == null) { |
| 236 | throw exception(USER_NOT_EXISTS); | 179 | throw exception(USER_NOT_EXISTS); |
| 237 | } | 180 | } |
| 238 | 181 | ||
| 239 | - // 创建 LoginUser 对象 | ||
| 240 | - LoginUser loginUser = buildLoginUser(user); | ||
| 241 | - | ||
| 242 | - // 缓存登录用户到 Redis 中,返回 Token 令牌 | ||
| 243 | - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); | 182 | + // 创建 Token 令牌,记录登录日志 |
| 183 | + return createTokenAfterLoginSuccess(user.getId(), null, LoginLogTypeEnum.LOGIN_SOCIAL); | ||
| 244 | } | 184 | } |
| 245 | 185 | ||
| 246 | @Override | 186 | @Override |
| 247 | - public String socialBindLogin(AuthSocialBindLoginReqVO reqVO, String userIp, String userAgent) { | 187 | + public AuthLoginRespVO socialBindLogin(AuthSocialBindLoginReqVO reqVO) { |
| 248 | // 使用账号密码,进行登录。 | 188 | // 使用账号密码,进行登录。 |
| 249 | - LoginUser loginUser = login0(reqVO.getUsername(), reqVO.getPassword()); | 189 | + AdminUserDO user = login0(reqVO.getUsername(), reqVO.getPassword()); |
| 250 | 190 | ||
| 251 | // 绑定社交用户 | 191 | // 绑定社交用户 |
| 252 | - socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(loginUser.getId(), getUserType().getValue(), reqVO)); | 192 | + socialUserService.bindSocialUser(AuthConvert.INSTANCE.convert(user.getId(), getUserType().getValue(), reqVO)); |
| 253 | 193 | ||
| 254 | - // 缓存登录用户到 Redis 中,返回 Token 令牌 | ||
| 255 | - return createUserSessionAfterLoginSuccess(loginUser, LoginLogTypeEnum.LOGIN_SOCIAL, userIp, userAgent); | 194 | + // 创建 Token 令牌,记录登录日志 |
| 195 | + return createTokenAfterLoginSuccess(user.getId(), reqVO.getUsername(), LoginLogTypeEnum.LOGIN_SOCIAL); | ||
| 256 | } | 196 | } |
| 257 | 197 | ||
| 258 | - private String createUserSessionAfterLoginSuccess(LoginUser loginUser, LoginLogTypeEnum logType, String userIp, String userAgent) { | 198 | + @Override |
| 199 | + public AuthLoginRespVO refreshToken(String refreshToken) { | ||
| 200 | + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, OAuth2ClientIdEnum.DEFAULT.getId()); | ||
| 201 | + return AuthConvert.INSTANCE.convert(accessTokenDO); | ||
| 202 | + } | ||
| 203 | + | ||
| 204 | + private AuthLoginRespVO createTokenAfterLoginSuccess(Long userId, String username, LoginLogTypeEnum logType) { | ||
| 259 | // 插入登陆日志 | 205 | // 插入登陆日志 |
| 260 | - createLoginLog(loginUser.getUsername(), logType, LoginResultEnum.SUCCESS); | ||
| 261 | - // 缓存登录用户到 Redis 中,返回 Token 令牌 | ||
| 262 | - return userSessionService.createUserSession(loginUser, userIp, userAgent); | 206 | + createLoginLog(userId, username, logType, LoginResultEnum.SUCCESS); |
| 207 | + // 创建访问令牌 | ||
| 208 | + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, getUserType().getValue(), | ||
| 209 | + OAuth2ClientIdEnum.DEFAULT.getId()); | ||
| 210 | + // 构建返回结果 | ||
| 211 | + return AuthConvert.INSTANCE.convert(accessTokenDO); | ||
| 263 | } | 212 | } |
| 264 | 213 | ||
| 265 | @Override | 214 | @Override |
| 266 | - public void logout(String token) { | ||
| 267 | - // 查询用户信息 | ||
| 268 | - LoginUser loginUser = userSessionService.getLoginUser(token); | ||
| 269 | - if (loginUser == null) { | 215 | + public void logout(String token, Integer logType) { |
| 216 | + // 删除访问令牌 | ||
| 217 | + OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.removeAccessToken(token); | ||
| 218 | + if (accessTokenDO == null) { | ||
| 270 | return; | 219 | return; |
| 271 | } | 220 | } |
| 272 | - // 删除 session | ||
| 273 | - userSessionService.deleteUserSession(token); | ||
| 274 | - // 记录登出日志 | ||
| 275 | - createLogoutLog(loginUser.getId(), loginUser.getUsername()); | ||
| 276 | - } | ||
| 277 | - | ||
| 278 | - @Override | ||
| 279 | - public UserTypeEnum getUserType() { | ||
| 280 | - return UserTypeEnum.ADMIN; | 221 | + // 删除成功,则记录登出日志 |
| 222 | + createLogoutLog(accessTokenDO.getUserId(), accessTokenDO.getUserType(), logType); | ||
| 281 | } | 223 | } |
| 282 | 224 | ||
| 283 | - private void createLogoutLog(Long userId, String username) { | 225 | + private void createLogoutLog(Long userId, Integer userType, Integer logType) { |
| 284 | LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); | 226 | LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); |
| 285 | - reqDTO.setLogType(LoginLogTypeEnum.LOGOUT_SELF.getType()); | 227 | + reqDTO.setLogType(logType); |
| 286 | reqDTO.setTraceId(TracerUtils.getTraceId()); | 228 | reqDTO.setTraceId(TracerUtils.getTraceId()); |
| 287 | reqDTO.setUserId(userId); | 229 | reqDTO.setUserId(userId); |
| 288 | - reqDTO.setUserType(getUserType().getValue()); | ||
| 289 | - reqDTO.setUsername(username); | 230 | + reqDTO.setUserType(userType); |
| 231 | + if (ObjectUtil.notEqual(getUserType(), userType)) { | ||
| 232 | + reqDTO.setUsername(getUsername(userId)); | ||
| 233 | + } else { | ||
| 234 | + reqDTO.setUsername(memberService.getMemberUserMobile(userId)); | ||
| 235 | + } | ||
| 290 | reqDTO.setUserAgent(ServletUtils.getUserAgent()); | 236 | reqDTO.setUserAgent(ServletUtils.getUserAgent()); |
| 291 | reqDTO.setUserIp(ServletUtils.getClientIP()); | 237 | reqDTO.setUserIp(ServletUtils.getClientIP()); |
| 292 | reqDTO.setResult(LoginResultEnum.SUCCESS.getResult()); | 238 | reqDTO.setResult(LoginResultEnum.SUCCESS.getResult()); |
| 293 | loginLogService.createLoginLog(reqDTO); | 239 | loginLogService.createLoginLog(reqDTO); |
| 294 | } | 240 | } |
| 295 | 241 | ||
| 296 | - @Override | ||
| 297 | - public LoginUser verifyTokenAndRefresh(String token) { | ||
| 298 | - // 获得 LoginUser | ||
| 299 | - LoginUser loginUser = userSessionService.getLoginUser(token); | ||
| 300 | - if (loginUser == null) { | 242 | + private String getUsername(Long userId) { |
| 243 | + if (userId == null) { | ||
| 301 | return null; | 244 | return null; |
| 302 | } | 245 | } |
| 303 | - // 刷新 LoginUser 缓存 | ||
| 304 | - return this.refreshLoginUserCache(token, loginUser); | ||
| 305 | - } | ||
| 306 | - | ||
| 307 | - private LoginUser refreshLoginUserCache(String token, LoginUser loginUser) { | ||
| 308 | - // 每 1/3 的 Session 超时时间,刷新 LoginUser 缓存 | ||
| 309 | - if (System.currentTimeMillis() - loginUser.getUpdateTime().getTime() < | ||
| 310 | - userSessionService.getSessionTimeoutMillis() / 3) { | ||
| 311 | - return loginUser; | ||
| 312 | - } | ||
| 313 | - | ||
| 314 | - // 重新加载 AdminUserDO 信息 | ||
| 315 | - AdminUserDO user = userService.getUser(loginUser.getId()); | ||
| 316 | - if (user == null || CommonStatusEnum.DISABLE.getStatus().equals(user.getStatus())) { | ||
| 317 | - throw exception(AUTH_TOKEN_EXPIRED); // 校验 token 时,用户被禁用的情况下,也认为 token 过期,方便前端跳转到登录界面 | ||
| 318 | - } | ||
| 319 | - | ||
| 320 | - // 刷新 LoginUser 缓存 | ||
| 321 | - LoginUser newLoginUser= this.buildLoginUser(user); | ||
| 322 | - userSessionService.refreshUserSession(token, newLoginUser); | ||
| 323 | - return newLoginUser; | 246 | + AdminUserDO user = userService.getUser(userId); |
| 247 | + return user != null ? user.getUsername() : null; | ||
| 324 | } | 248 | } |
| 325 | 249 | ||
| 326 | - private LoginUser buildLoginUser(AdminUserDO user) { | ||
| 327 | - LoginUser loginUser = AuthConvert.INSTANCE.convert(user); | ||
| 328 | - // 补全字段 | ||
| 329 | - loginUser.setDeptId(user.getDeptId()); | ||
| 330 | - loginUser.setRoleIds(this.getUserRoleIds(loginUser.getId())); | ||
| 331 | - return loginUser; | 250 | + private UserTypeEnum getUserType() { |
| 251 | + return UserTypeEnum.ADMIN; | ||
| 332 | } | 252 | } |
| 333 | 253 | ||
| 334 | } | 254 | } |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/AdminOAuth2Service.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.service.auth; | ||
| 2 | + | ||
| 3 | +/** | ||
| 4 | + * 管理后台的 OAuth2 Service 接口 | ||
| 5 | + * | ||
| 6 | + * 将自身的 AdminUser 用户,授权给第三方应用,采用 OAuth2.0 的协议。 | ||
| 7 | + * | ||
| 8 | + * 问题:为什么自身也作为一个第三方应用,也走这套流程呢? | ||
| 9 | + * 回复:当然可以这么做,采用 Implicit 模式。考虑到大多数开发者使用不到这个特性,OAuth2.0 毕竟有一定学习成本,所以暂时没有采取这种方式。 | ||
| 10 | + * | ||
| 11 | + * @author 芋道源码 | ||
| 12 | + */ | ||
| 13 | +public interface AdminOAuth2Service { | ||
| 14 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.service.auth; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientCreateReqVO; | ||
| 5 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientPageReqVO; | ||
| 6 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientUpdateReqVO; | ||
| 7 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; | ||
| 8 | + | ||
| 9 | +import javax.validation.Valid; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * OAuth2.0 Client Service 接口 | ||
| 13 | + * | ||
| 14 | + * 从功能上,和 JdbcClientDetailsService 的功能,提供客户端的操作 | ||
| 15 | + * | ||
| 16 | + * @author 芋道源码 | ||
| 17 | + */ | ||
| 18 | +public interface OAuth2ClientService { | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * 创建OAuth2 客户端 | ||
| 22 | + * | ||
| 23 | + * @param createReqVO 创建信息 | ||
| 24 | + * @return 编号 | ||
| 25 | + */ | ||
| 26 | + Long createOAuth2Client(@Valid OAuth2ClientCreateReqVO createReqVO); | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 更新OAuth2 客户端 | ||
| 30 | + * | ||
| 31 | + * @param updateReqVO 更新信息 | ||
| 32 | + */ | ||
| 33 | + void updateOAuth2Client(@Valid OAuth2ClientUpdateReqVO updateReqVO); | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 删除OAuth2 客户端 | ||
| 37 | + * | ||
| 38 | + * @param id 编号 | ||
| 39 | + */ | ||
| 40 | + void deleteOAuth2Client(Long id); | ||
| 41 | + | ||
| 42 | + /** | ||
| 43 | + * 获得OAuth2 客户端 | ||
| 44 | + * | ||
| 45 | + * @param id 编号 | ||
| 46 | + * @return OAuth2 客户端 | ||
| 47 | + */ | ||
| 48 | + OAuth2ClientDO getOAuth2Client(Long id); | ||
| 49 | + | ||
| 50 | + /** | ||
| 51 | + * 获得OAuth2 客户端分页 | ||
| 52 | + * | ||
| 53 | + * @param pageReqVO 分页查询 | ||
| 54 | + * @return OAuth2 客户端分页 | ||
| 55 | + */ | ||
| 56 | + PageResult<OAuth2ClientDO> getOAuth2ClientPage(OAuth2ClientPageReqVO pageReqVO); | ||
| 57 | + | ||
| 58 | + /** | ||
| 59 | + * 从缓存中,校验客户端是否合法 | ||
| 60 | + * | ||
| 61 | + * @param id 客户端编号 | ||
| 62 | + * @return 客户端 | ||
| 63 | + */ | ||
| 64 | + OAuth2ClientDO validOAuthClientFromCache(Long id); | ||
| 65 | + | ||
| 66 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.service.auth; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientCreateReqVO; | ||
| 5 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientPageReqVO; | ||
| 6 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientUpdateReqVO; | ||
| 7 | +import cn.iocoder.yudao.module.system.convert.auth.OAuth2ClientConvert; | ||
| 8 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; | ||
| 9 | +import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2ClientMapper; | ||
| 10 | +import org.springframework.stereotype.Service; | ||
| 11 | + | ||
| 12 | +import javax.annotation.Resource; | ||
| 13 | + | ||
| 14 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 15 | +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CLIENT_NOT_EXISTS; | ||
| 16 | + | ||
| 17 | +/** | ||
| 18 | + * OAuth2.0 Client Service 实现类 | ||
| 19 | + * | ||
| 20 | + * @author 芋道源码 | ||
| 21 | + */ | ||
| 22 | +@Service | ||
| 23 | +public class OAuth2ClientServiceImpl implements OAuth2ClientService { | ||
| 24 | + | ||
| 25 | + @Resource | ||
| 26 | + private OAuth2ClientMapper oauth2ClientMapper; | ||
| 27 | + | ||
| 28 | + @Override | ||
| 29 | + public Long createOAuth2Client(OAuth2ClientCreateReqVO createReqVO) { | ||
| 30 | + // 插入 | ||
| 31 | + OAuth2ClientDO oAuth2Client = OAuth2ClientConvert.INSTANCE.convert(createReqVO); | ||
| 32 | + oauth2ClientMapper.insert(oAuth2Client); | ||
| 33 | + // 返回 | ||
| 34 | + return oAuth2Client.getId(); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + @Override | ||
| 38 | + public void updateOAuth2Client(OAuth2ClientUpdateReqVO updateReqVO) { | ||
| 39 | + // 校验存在 | ||
| 40 | + this.validateOAuth2ClientExists(updateReqVO.getId()); | ||
| 41 | + // 更新 | ||
| 42 | + OAuth2ClientDO updateObj = OAuth2ClientConvert.INSTANCE.convert(updateReqVO); | ||
| 43 | + oauth2ClientMapper.updateById(updateObj); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + @Override | ||
| 47 | + public void deleteOAuth2Client(Long id) { | ||
| 48 | + // 校验存在 | ||
| 49 | + this.validateOAuth2ClientExists(id); | ||
| 50 | + // 删除 | ||
| 51 | + oauth2ClientMapper.deleteById(id); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + private void validateOAuth2ClientExists(Long id) { | ||
| 55 | + if (oauth2ClientMapper.selectById(id) == null) { | ||
| 56 | + throw exception(OAUTH2_CLIENT_NOT_EXISTS); | ||
| 57 | + } | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + @Override | ||
| 61 | + public OAuth2ClientDO getOAuth2Client(Long id) { | ||
| 62 | + return oauth2ClientMapper.selectById(id); | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + @Override | ||
| 66 | + public PageResult<OAuth2ClientDO> getOAuth2ClientPage(OAuth2ClientPageReqVO pageReqVO) { | ||
| 67 | + return oauth2ClientMapper.selectPage(pageReqVO); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + @Override | ||
| 71 | + public OAuth2ClientDO validOAuthClientFromCache(Long id) { | ||
| 72 | + return new OAuth2ClientDO().setId(id) | ||
| 73 | + .setAccessTokenValiditySeconds(60 * 30) | ||
| 74 | + .setRefreshTokenValiditySeconds(60 * 60 * 24 * 30); | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2CodeService.java
0 → 100644
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.service.auth; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO; | ||
| 5 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * OAuth2.0 Token Service 接口 | ||
| 9 | + * | ||
| 10 | + * 从功能上,和 Spring Security OAuth 的 DefaultTokenServices + JdbcTokenStore 的功能,提供访问令牌、刷新令牌的操作 | ||
| 11 | + * | ||
| 12 | + * @author 芋道源码 | ||
| 13 | + */ | ||
| 14 | +public interface OAuth2TokenService { | ||
| 15 | + | ||
| 16 | + /** | ||
| 17 | + * 创建访问令牌 | ||
| 18 | + * 注意:该流程中,会包含创建刷新令牌的创建 | ||
| 19 | + * | ||
| 20 | + * 参考 DefaultTokenServices 的 createAccessToken 方法 | ||
| 21 | + * | ||
| 22 | + * @param userId 用户编号 | ||
| 23 | + * @param userType 用户类型 | ||
| 24 | + * @param clientId 客户端编号 | ||
| 25 | + * @return 访问令牌的信息 | ||
| 26 | + */ | ||
| 27 | + OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, Long clientId); | ||
| 28 | + | ||
| 29 | + /** | ||
| 30 | + * 刷新访问令牌 | ||
| 31 | + * | ||
| 32 | + * 参考 DefaultTokenServices 的 refreshAccessToken 方法 | ||
| 33 | + * | ||
| 34 | + * @param refreshToken 刷新令牌 | ||
| 35 | + * @param clientId 客户端编号 | ||
| 36 | + * @return 访问令牌的信息 | ||
| 37 | + */ | ||
| 38 | + OAuth2AccessTokenDO refreshAccessToken(String refreshToken, Long clientId); | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * 获得访问令牌 | ||
| 42 | + * | ||
| 43 | + * 参考 DefaultTokenServices 的 getAccessToken 方法 | ||
| 44 | + * | ||
| 45 | + * @param accessToken 访问令牌 | ||
| 46 | + * @return 访问令牌的信息 | ||
| 47 | + */ | ||
| 48 | + OAuth2AccessTokenDO getAccessToken(String accessToken); | ||
| 49 | + | ||
| 50 | + /** | ||
| 51 | + * 校验访问令牌 | ||
| 52 | + * | ||
| 53 | + * @param accessToken 访问令牌 | ||
| 54 | + * @return 访问令牌的信息 | ||
| 55 | + */ | ||
| 56 | + OAuth2AccessTokenDO checkAccessToken(String accessToken); | ||
| 57 | + | ||
| 58 | + /** | ||
| 59 | + * 移除访问令牌 | ||
| 60 | + * 注意:该流程中,会移除相关的刷新令牌 | ||
| 61 | + * | ||
| 62 | + * 参考 DefaultTokenServices 的 revokeToken 方法 | ||
| 63 | + * | ||
| 64 | + * @param accessToken 刷新令牌 | ||
| 65 | + * @return 访问令牌的信息 | ||
| 66 | + */ | ||
| 67 | + OAuth2AccessTokenDO removeAccessToken(String accessToken); | ||
| 68 | + | ||
| 69 | + /** | ||
| 70 | + * 获得访问令牌分页 | ||
| 71 | + * | ||
| 72 | + * @param reqVO 请求 | ||
| 73 | + * @return 访问令牌分页 | ||
| 74 | + */ | ||
| 75 | + PageResult<OAuth2AccessTokenDO> getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO); | ||
| 76 | + | ||
| 77 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/OAuth2TokenServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.service.auth; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 4 | +import cn.hutool.core.util.IdUtil; | ||
| 5 | +import cn.hutool.core.util.ObjectUtil; | ||
| 6 | +import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; | ||
| 7 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 8 | +import cn.iocoder.yudao.framework.common.util.date.DateUtils; | ||
| 9 | +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | ||
| 10 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.token.OAuth2AccessTokenPageReqVO; | ||
| 11 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; | ||
| 12 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; | ||
| 13 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2RefreshTokenDO; | ||
| 14 | +import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2AccessTokenMapper; | ||
| 15 | +import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2RefreshTokenMapper; | ||
| 16 | +import cn.iocoder.yudao.module.system.dal.redis.auth.OAuth2AccessTokenRedisDAO; | ||
| 17 | +import org.springframework.stereotype.Service; | ||
| 18 | +import org.springframework.transaction.annotation.Transactional; | ||
| 19 | + | ||
| 20 | +import javax.annotation.Resource; | ||
| 21 | +import java.util.Calendar; | ||
| 22 | +import java.util.List; | ||
| 23 | + | ||
| 24 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 25 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | ||
| 26 | + | ||
| 27 | +/** | ||
| 28 | + * OAuth2.0 Token Service 实现类 | ||
| 29 | + * | ||
| 30 | + * @author 芋道源码 | ||
| 31 | + */ | ||
| 32 | +@Service | ||
| 33 | +public class OAuth2TokenServiceImpl implements OAuth2TokenService { | ||
| 34 | + | ||
| 35 | + @Resource | ||
| 36 | + private OAuth2AccessTokenMapper oauth2AccessTokenMapper; | ||
| 37 | + @Resource | ||
| 38 | + private OAuth2RefreshTokenMapper oauth2RefreshTokenMapper; | ||
| 39 | + | ||
| 40 | + @Resource | ||
| 41 | + private OAuth2AccessTokenRedisDAO oauth2AccessTokenRedisDAO; | ||
| 42 | + | ||
| 43 | + @Resource | ||
| 44 | + private OAuth2ClientService oauth2ClientService; | ||
| 45 | + | ||
| 46 | + @Override | ||
| 47 | + @Transactional | ||
| 48 | + public OAuth2AccessTokenDO createAccessToken(Long userId, Integer userType, Long clientId) { | ||
| 49 | + OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId); | ||
| 50 | + // 创建刷新令牌 | ||
| 51 | + OAuth2RefreshTokenDO refreshTokenDO = createOAuth2RefreshToken(userId, userType, clientDO); | ||
| 52 | + // 创建访问令牌 | ||
| 53 | + return createOAuth2AccessToken(refreshTokenDO, clientDO); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + @Override | ||
| 57 | + public OAuth2AccessTokenDO refreshAccessToken(String refreshToken, Long clientId) { | ||
| 58 | + // 查询访问令牌 | ||
| 59 | + OAuth2RefreshTokenDO refreshTokenDO = oauth2RefreshTokenMapper.selectByRefreshToken(refreshToken); | ||
| 60 | + if (refreshTokenDO == null) { | ||
| 61 | + throw exception(GlobalErrorCodeConstants.BAD_REQUEST, "无效的刷新令牌"); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + // 校验 Client 匹配 | ||
| 65 | + OAuth2ClientDO clientDO = oauth2ClientService.validOAuthClientFromCache(clientId); | ||
| 66 | + if (ObjectUtil.notEqual(clientId, refreshTokenDO.getClientId())) { | ||
| 67 | + throw exception(GlobalErrorCodeConstants.BAD_REQUEST, "刷新令牌的客户端编号不正确"); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + // 移除相关的访问令牌 | ||
| 71 | + List<OAuth2AccessTokenDO> accessTokenDOs = oauth2AccessTokenMapper.selectListByRefreshToken(refreshToken); | ||
| 72 | + if (CollUtil.isNotEmpty(accessTokenDOs)) { | ||
| 73 | + oauth2AccessTokenMapper.deleteBatchIds(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getId)); | ||
| 74 | + oauth2AccessTokenRedisDAO.deleteList(convertSet(accessTokenDOs, OAuth2AccessTokenDO::getAccessToken)); | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + // 已过期的情况下,删除刷新令牌 | ||
| 78 | + if (DateUtils.isExpired(refreshTokenDO.getExpiresTime())) { | ||
| 79 | + oauth2AccessTokenMapper.deleteById(refreshTokenDO.getId()); | ||
| 80 | + throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "刷新令牌已过期"); | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + // 创建访问令牌 | ||
| 84 | + return createOAuth2AccessToken(refreshTokenDO, clientDO); | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + @Override | ||
| 88 | + public OAuth2AccessTokenDO getAccessToken(String accessToken) { | ||
| 89 | + // 优先从 Redis 中获取 | ||
| 90 | + OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenRedisDAO.get(accessToken); | ||
| 91 | + if (accessTokenDO != null) { | ||
| 92 | + return accessTokenDO; | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + // 获取不到,从 MySQL 中获取 | ||
| 96 | + accessTokenDO = oauth2AccessTokenMapper.selectById(accessToken); | ||
| 97 | + // 如果在 MySQL 存在,则往 Redis 中写入 | ||
| 98 | + if (accessTokenDO != null && !DateUtils.isExpired(accessTokenDO.getExpiresTime())) { | ||
| 99 | + oauth2AccessTokenRedisDAO.set(accessTokenDO); | ||
| 100 | + } | ||
| 101 | + return accessTokenDO; | ||
| 102 | + } | ||
| 103 | + | ||
| 104 | + @Override | ||
| 105 | + public OAuth2AccessTokenDO checkAccessToken(String accessToken) { | ||
| 106 | + OAuth2AccessTokenDO accessTokenDO = getAccessToken(accessToken); | ||
| 107 | + if (accessTokenDO == null) { | ||
| 108 | + throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "访问令牌不存在"); | ||
| 109 | + } | ||
| 110 | + if (DateUtils.isExpired(accessTokenDO.getExpiresTime())) { | ||
| 111 | + throw exception(GlobalErrorCodeConstants.UNAUTHORIZED, "访问令牌已过期"); | ||
| 112 | + } | ||
| 113 | + return accessTokenDO; | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + @Override | ||
| 117 | + public OAuth2AccessTokenDO removeAccessToken(String accessToken) { | ||
| 118 | + // 删除访问令牌 | ||
| 119 | + OAuth2AccessTokenDO accessTokenDO = oauth2AccessTokenMapper.selectByAccessToken(accessToken); | ||
| 120 | + if (accessTokenDO == null) { | ||
| 121 | + return null; | ||
| 122 | + } | ||
| 123 | + oauth2AccessTokenMapper.deleteById(accessTokenDO.getId()); | ||
| 124 | + oauth2AccessTokenRedisDAO.delete(accessToken); | ||
| 125 | + // 删除刷新令牌 | ||
| 126 | + oauth2RefreshTokenMapper.deleteByRefreshToken(accessTokenDO.getRefreshToken()); | ||
| 127 | + return accessTokenDO; | ||
| 128 | + } | ||
| 129 | + | ||
| 130 | + @Override | ||
| 131 | + public PageResult<OAuth2AccessTokenDO> getAccessTokenPage(OAuth2AccessTokenPageReqVO reqVO) { | ||
| 132 | + return oauth2AccessTokenMapper.selectPage(reqVO); | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2RefreshTokenDO refreshTokenDO, OAuth2ClientDO clientDO) { | ||
| 136 | + OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken()) | ||
| 137 | + .setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType()).setClientId(clientDO.getId()) | ||
| 138 | + .setRefreshToken(refreshTokenDO.getRefreshToken()) | ||
| 139 | + .setExpiresTime(DateUtils.addDate(Calendar.SECOND, clientDO.getAccessTokenValiditySeconds())); | ||
| 140 | + accessTokenDO.setTenantId(TenantContextHolder.getTenantId()); // 手动设置租户编号,避免缓存到 Redis 的时候,无对应的租户编号 | ||
| 141 | + oauth2AccessTokenMapper.insert(accessTokenDO); | ||
| 142 | + // 记录到 Redis 中 | ||
| 143 | + oauth2AccessTokenRedisDAO.set(accessTokenDO); | ||
| 144 | + return accessTokenDO; | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + private OAuth2RefreshTokenDO createOAuth2RefreshToken(Long userId, Integer userType, OAuth2ClientDO clientDO) { | ||
| 148 | + OAuth2RefreshTokenDO refreshToken = new OAuth2RefreshTokenDO().setRefreshToken(generateRefreshToken()) | ||
| 149 | + .setUserId(userId).setUserType(userType).setClientId(clientDO.getId()) | ||
| 150 | + .setExpiresTime(DateUtils.addDate(Calendar.SECOND, clientDO.getRefreshTokenValiditySeconds())); | ||
| 151 | + oauth2RefreshTokenMapper.insert(refreshToken); | ||
| 152 | + return refreshToken; | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + private static String generateAccessToken() { | ||
| 156 | + return IdUtil.fastSimpleUUID(); | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + private static String generateRefreshToken() { | ||
| 160 | + return IdUtil.fastSimpleUUID(); | ||
| 161 | + } | ||
| 162 | + | ||
| 163 | +} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/UserSessionService.java deleted
| 1 | -package cn.iocoder.yudao.module.system.service.auth; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 4 | -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO; | ||
| 5 | -import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO; | ||
| 6 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 7 | - | ||
| 8 | -/** | ||
| 9 | - * 在线用户 Session Service 接口 | ||
| 10 | - * | ||
| 11 | - * @author 芋道源码 | ||
| 12 | - */ | ||
| 13 | -public interface UserSessionService { | ||
| 14 | - | ||
| 15 | - /** | ||
| 16 | - * 获得在线用户分页列表 | ||
| 17 | - * | ||
| 18 | - * @param reqVO 分页条件 | ||
| 19 | - * @return 份额与列表 | ||
| 20 | - */ | ||
| 21 | - PageResult<UserSessionDO> getUserSessionPage(UserSessionPageReqVO reqVO); | ||
| 22 | - | ||
| 23 | - /** | ||
| 24 | - * 移除超时的在线用户 | ||
| 25 | - * | ||
| 26 | - * @return {@link Long } 移出的超时用户数量 | ||
| 27 | - **/ | ||
| 28 | - long deleteTimeoutSession(); | ||
| 29 | - | ||
| 30 | - /** | ||
| 31 | - * 创建在线用户 Session | ||
| 32 | - * | ||
| 33 | - * @param loginUser 登录用户 | ||
| 34 | - * @param userIp 用户 IP | ||
| 35 | - * @param userAgent 用户 UA | ||
| 36 | - * @return Token 令牌 | ||
| 37 | - */ | ||
| 38 | - String createUserSession(LoginUser loginUser, String userIp, String userAgent); | ||
| 39 | - | ||
| 40 | - /** | ||
| 41 | - * 刷新在线用户 Session 的更新时间 | ||
| 42 | - * | ||
| 43 | - * @param token 令牌 | ||
| 44 | - * @param loginUser 登录用户 | ||
| 45 | - */ | ||
| 46 | - void refreshUserSession(String token, LoginUser loginUser); | ||
| 47 | - | ||
| 48 | - /** | ||
| 49 | - * 删除在线用户 Session | ||
| 50 | - * | ||
| 51 | - * @param token token 令牌 | ||
| 52 | - */ | ||
| 53 | - void deleteUserSession(String token); | ||
| 54 | - | ||
| 55 | - /** | ||
| 56 | - * 删除在线用户 Session | ||
| 57 | - * | ||
| 58 | - * @param id 编号 | ||
| 59 | - */ | ||
| 60 | - void deleteUserSession(Long id); | ||
| 61 | - | ||
| 62 | - /** | ||
| 63 | - * 获得 Token 对应的在线用户 | ||
| 64 | - * | ||
| 65 | - * @param token 令牌 | ||
| 66 | - * @return 在线用户 | ||
| 67 | - */ | ||
| 68 | - LoginUser getLoginUser(String token); | ||
| 69 | - | ||
| 70 | - /** | ||
| 71 | - * 获得 Session 超时时间,单位:毫秒 | ||
| 72 | - * | ||
| 73 | - * @return 超时时间 | ||
| 74 | - */ | ||
| 75 | - Long getSessionTimeoutMillis(); | ||
| 76 | - | ||
| 77 | -} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImpl.java deleted
| 1 | -package cn.iocoder.yudao.module.system.service.auth; | ||
| 2 | - | ||
| 3 | -import cn.hutool.core.collection.CollUtil; | ||
| 4 | -import cn.hutool.core.util.IdUtil; | ||
| 5 | -import cn.hutool.core.util.StrUtil; | ||
| 6 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 7 | -import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; | ||
| 8 | -import cn.iocoder.yudao.framework.security.config.SecurityProperties; | ||
| 9 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 10 | -import cn.iocoder.yudao.module.system.api.logger.dto.LoginLogCreateReqDTO; | ||
| 11 | -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO; | ||
| 12 | -import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO; | ||
| 13 | -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | ||
| 14 | -import cn.iocoder.yudao.module.system.dal.mysql.auth.UserSessionMapper; | ||
| 15 | -import cn.iocoder.yudao.module.system.dal.redis.auth.LoginUserRedisDAO; | ||
| 16 | -import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; | ||
| 17 | -import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; | ||
| 18 | -import cn.iocoder.yudao.module.system.service.logger.LoginLogService; | ||
| 19 | -import cn.iocoder.yudao.module.system.service.user.AdminUserService; | ||
| 20 | -import lombok.extern.slf4j.Slf4j; | ||
| 21 | -import org.springframework.stereotype.Service; | ||
| 22 | - | ||
| 23 | -import javax.annotation.Resource; | ||
| 24 | -import java.time.Duration; | ||
| 25 | -import java.util.Collection; | ||
| 26 | -import java.util.Date; | ||
| 27 | -import java.util.List; | ||
| 28 | - | ||
| 29 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | ||
| 30 | -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime; | ||
| 31 | - | ||
| 32 | -/** | ||
| 33 | - * 在线用户 Session Service 实现类 | ||
| 34 | - * | ||
| 35 | - * @author 芋道源码 | ||
| 36 | - */ | ||
| 37 | -@Slf4j | ||
| 38 | -@Service | ||
| 39 | -public class UserSessionServiceImpl implements UserSessionService { | ||
| 40 | - | ||
| 41 | - @Resource | ||
| 42 | - private UserSessionMapper userSessionMapper; | ||
| 43 | - | ||
| 44 | - @Resource | ||
| 45 | - private AdminUserService userService; | ||
| 46 | - @Resource | ||
| 47 | - private LoginLogService loginLogService; | ||
| 48 | - | ||
| 49 | - @Resource | ||
| 50 | - private LoginUserRedisDAO loginUserRedisDAO; | ||
| 51 | - | ||
| 52 | - @Resource | ||
| 53 | - private SecurityProperties securityProperties; | ||
| 54 | - | ||
| 55 | - @Override | ||
| 56 | - public PageResult<UserSessionDO> getUserSessionPage(UserSessionPageReqVO reqVO) { | ||
| 57 | - // 处理基于用户昵称的查询 | ||
| 58 | - Collection<Long> userIds = null; | ||
| 59 | - if (StrUtil.isNotEmpty(reqVO.getUsername())) { | ||
| 60 | - userIds = convertSet(userService.getUsersByUsername(reqVO.getUsername()), AdminUserDO::getId); | ||
| 61 | - if (CollUtil.isEmpty(userIds)) { | ||
| 62 | - return PageResult.empty(); | ||
| 63 | - } | ||
| 64 | - } | ||
| 65 | - return userSessionMapper.selectPage(reqVO, userIds); | ||
| 66 | - } | ||
| 67 | - | ||
| 68 | - @Override | ||
| 69 | - public long deleteTimeoutSession() { | ||
| 70 | - // 获取 db 里已经超时的用户列表 | ||
| 71 | - List<UserSessionDO> timeoutSessions = userSessionMapper.selectListBySessionTimoutLt(); | ||
| 72 | - if (CollUtil.isEmpty(timeoutSessions)) { | ||
| 73 | - return 0L; | ||
| 74 | - } | ||
| 75 | - | ||
| 76 | - // 由于过期的用户一般不多,所以顺序遍历,进行清理 | ||
| 77 | - int count = 0; | ||
| 78 | - for (UserSessionDO session : timeoutSessions) { | ||
| 79 | - // 基于 Redis 二次判断,同时也保证 Redis Key 的立即过期,避免延迟导致浪费内存空间 | ||
| 80 | - if (loginUserRedisDAO.exists(session.getToken())) { | ||
| 81 | - continue; | ||
| 82 | - } | ||
| 83 | - userSessionMapper.deleteById(session.getId()); | ||
| 84 | - // 记录退出日志 | ||
| 85 | - createLogoutLog(session, LoginLogTypeEnum.LOGOUT_TIMEOUT); | ||
| 86 | - count++; | ||
| 87 | - } | ||
| 88 | - return count; | ||
| 89 | - } | ||
| 90 | - | ||
| 91 | - private void createLogoutLog(UserSessionDO session, LoginLogTypeEnum type) { | ||
| 92 | - LoginLogCreateReqDTO reqDTO = new LoginLogCreateReqDTO(); | ||
| 93 | - reqDTO.setLogType(type.getType()); | ||
| 94 | - reqDTO.setTraceId(TracerUtils.getTraceId()); | ||
| 95 | - reqDTO.setUserId(session.getUserId()); | ||
| 96 | - reqDTO.setUserType(session.getUserType()); | ||
| 97 | - reqDTO.setUsername(session.getUsername()); | ||
| 98 | - reqDTO.setUserAgent(session.getUserAgent()); | ||
| 99 | - reqDTO.setUserIp(session.getUserIp()); | ||
| 100 | - reqDTO.setResult(LoginResultEnum.SUCCESS.getResult()); | ||
| 101 | - loginLogService.createLoginLog(reqDTO); | ||
| 102 | - } | ||
| 103 | - | ||
| 104 | - @Override | ||
| 105 | - public String createUserSession(LoginUser loginUser, String userIp, String userAgent) { | ||
| 106 | - // 生成 Session 编号 | ||
| 107 | - String token = generateToken(); | ||
| 108 | - // 写入 Redis 缓存 | ||
| 109 | - loginUser.setUpdateTime(new Date()); | ||
| 110 | - loginUserRedisDAO.set(token, loginUser); | ||
| 111 | - // 写入 DB 中 | ||
| 112 | - UserSessionDO userSession = UserSessionDO.builder().token(token) | ||
| 113 | - .userId(loginUser.getId()).userType(loginUser.getUserType()) | ||
| 114 | - .userIp(userIp).userAgent(userAgent).username(loginUser.getUsername()) | ||
| 115 | - .sessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis()))) | ||
| 116 | - .build(); | ||
| 117 | - userSessionMapper.insert(userSession); | ||
| 118 | - // 返回 Token 令牌 | ||
| 119 | - return token; | ||
| 120 | - } | ||
| 121 | - | ||
| 122 | - @Override | ||
| 123 | - public void refreshUserSession(String token, LoginUser loginUser) { | ||
| 124 | - // 写入 Redis 缓存 | ||
| 125 | - loginUser.setUpdateTime(new Date()); | ||
| 126 | - loginUserRedisDAO.set(token, loginUser); | ||
| 127 | - // 更新 DB 中 | ||
| 128 | - UserSessionDO updateObj = UserSessionDO.builder().build(); | ||
| 129 | - updateObj.setUsername(loginUser.getUsername()); | ||
| 130 | - updateObj.setUpdateTime(new Date()); | ||
| 131 | - updateObj.setSessionTimeout(addTime(Duration.ofMillis(getSessionTimeoutMillis()))); | ||
| 132 | - userSessionMapper.updateByToken(token, updateObj); | ||
| 133 | - } | ||
| 134 | - | ||
| 135 | - @Override | ||
| 136 | - public void deleteUserSession(String token) { | ||
| 137 | - // 删除 Redis 缓存 | ||
| 138 | - loginUserRedisDAO.delete(token); | ||
| 139 | - // 删除 DB 记录 | ||
| 140 | - userSessionMapper.deleteByToken(token); | ||
| 141 | - // 无需记录日志,因为退出那已经记录 | ||
| 142 | - } | ||
| 143 | - | ||
| 144 | - @Override | ||
| 145 | - public void deleteUserSession(Long id) { | ||
| 146 | - UserSessionDO session = userSessionMapper.selectById(id); | ||
| 147 | - if (session == null) { | ||
| 148 | - return; | ||
| 149 | - } | ||
| 150 | - // 删除 Redis 缓存 | ||
| 151 | - loginUserRedisDAO.delete(session.getToken()); | ||
| 152 | - // 删除 DB 记录 | ||
| 153 | - userSessionMapper.deleteById(id); | ||
| 154 | - // 记录退出日志 | ||
| 155 | - createLogoutLog(session, LoginLogTypeEnum.LOGOUT_DELETE); | ||
| 156 | - } | ||
| 157 | - | ||
| 158 | - @Override | ||
| 159 | - public LoginUser getLoginUser(String token) { | ||
| 160 | - return loginUserRedisDAO.get(token); | ||
| 161 | - } | ||
| 162 | - | ||
| 163 | - @Override | ||
| 164 | - public Long getSessionTimeoutMillis() { | ||
| 165 | - return securityProperties.getSessionTimeout().toMillis(); | ||
| 166 | - } | ||
| 167 | - | ||
| 168 | - /** | ||
| 169 | - * 生成 Token 令牌,目前采用 UUID 算法 | ||
| 170 | - * | ||
| 171 | - * @return Session 编号 | ||
| 172 | - */ | ||
| 173 | - private static String generateToken() { | ||
| 174 | - return IdUtil.fastSimpleUUID(); | ||
| 175 | - } | ||
| 176 | - | ||
| 177 | -} |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java
| @@ -3,12 +3,12 @@ package cn.iocoder.yudao.module.system.service.permission; | @@ -3,12 +3,12 @@ package cn.iocoder.yudao.module.system.service.permission; | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | import cn.hutool.core.collection.CollectionUtil; | 4 | import cn.hutool.core.collection.CollectionUtil; |
| 5 | import cn.hutool.core.util.ArrayUtil; | 5 | import cn.hutool.core.util.ArrayUtil; |
| 6 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 6 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | 7 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 7 | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; | 8 | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; |
| 8 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 9 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 10 | +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; | ||
| 9 | import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO; | 11 | import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO; |
| 10 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 11 | -import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; | ||
| 12 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; | 12 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; |
| 13 | import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; | 13 | import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; |
| 14 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; | 14 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; |
| @@ -22,6 +22,8 @@ import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper; | @@ -22,6 +22,8 @@ import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper; | ||
| 22 | import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; | 22 | import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; |
| 23 | import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer; | 23 | import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer; |
| 24 | import cn.iocoder.yudao.module.system.service.dept.DeptService; | 24 | import cn.iocoder.yudao.module.system.service.dept.DeptService; |
| 25 | +import cn.iocoder.yudao.module.system.service.user.AdminUserService; | ||
| 26 | +import com.google.common.base.Suppliers; | ||
| 25 | import com.google.common.collect.ImmutableMultimap; | 27 | import com.google.common.collect.ImmutableMultimap; |
| 26 | import com.google.common.collect.Multimap; | 28 | import com.google.common.collect.Multimap; |
| 27 | import com.google.common.collect.Sets; | 29 | import com.google.common.collect.Sets; |
| @@ -36,6 +38,10 @@ import org.springframework.transaction.support.TransactionSynchronizationManager | @@ -36,6 +38,10 @@ import org.springframework.transaction.support.TransactionSynchronizationManager | ||
| 36 | import javax.annotation.PostConstruct; | 38 | import javax.annotation.PostConstruct; |
| 37 | import javax.annotation.Resource; | 39 | import javax.annotation.Resource; |
| 38 | import java.util.*; | 40 | import java.util.*; |
| 41 | +import java.util.function.Supplier; | ||
| 42 | + | ||
| 43 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | ||
| 44 | +import static java.util.Collections.singleton; | ||
| 39 | 45 | ||
| 40 | /** | 46 | /** |
| 41 | * 权限 Service 实现类 | 47 | * 权限 Service 实现类 |
| @@ -47,11 +53,6 @@ import java.util.*; | @@ -47,11 +53,6 @@ import java.util.*; | ||
| 47 | public class PermissionServiceImpl implements PermissionService { | 53 | public class PermissionServiceImpl implements PermissionService { |
| 48 | 54 | ||
| 49 | /** | 55 | /** |
| 50 | - * LoginUser 的 Context 缓存 Key | ||
| 51 | - */ | ||
| 52 | - public static final String CONTEXT_KEY = PermissionServiceImpl.class.getSimpleName(); | ||
| 53 | - | ||
| 54 | - /** | ||
| 55 | * 定时执行 {@link #schedulePeriodicRefresh()} 的周期 | 56 | * 定时执行 {@link #schedulePeriodicRefresh()} 的周期 |
| 56 | * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高 | 57 | * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高 |
| 57 | */ | 58 | */ |
| @@ -93,6 +94,8 @@ public class PermissionServiceImpl implements PermissionService { | @@ -93,6 +94,8 @@ public class PermissionServiceImpl implements PermissionService { | ||
| 93 | private MenuService menuService; | 94 | private MenuService menuService; |
| 94 | @Resource | 95 | @Resource |
| 95 | private DeptService deptService; | 96 | private DeptService deptService; |
| 97 | + @Resource | ||
| 98 | + private AdminUserService userService; | ||
| 96 | 99 | ||
| 97 | @Resource | 100 | @Resource |
| 98 | private PermissionProducer permissionProducer; | 101 | private PermissionProducer permissionProducer; |
| @@ -319,7 +322,7 @@ public class PermissionServiceImpl implements PermissionService { | @@ -319,7 +322,7 @@ public class PermissionServiceImpl implements PermissionService { | ||
| 319 | } | 322 | } |
| 320 | 323 | ||
| 321 | // 获得当前登录的角色。如果为空,说明没有权限 | 324 | // 获得当前登录的角色。如果为空,说明没有权限 |
| 322 | - Set<Long> roleIds = SecurityFrameworkUtils.getLoginUserRoleIds(); | 325 | + Set<Long> roleIds = getUserRoleIds(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus())); |
| 323 | if (CollUtil.isEmpty(roleIds)) { | 326 | if (CollUtil.isEmpty(roleIds)) { |
| 324 | return false; | 327 | return false; |
| 325 | } | 328 | } |
| @@ -354,7 +357,7 @@ public class PermissionServiceImpl implements PermissionService { | @@ -354,7 +357,7 @@ public class PermissionServiceImpl implements PermissionService { | ||
| 354 | } | 357 | } |
| 355 | 358 | ||
| 356 | // 获得当前登录的角色。如果为空,说明没有权限 | 359 | // 获得当前登录的角色。如果为空,说明没有权限 |
| 357 | - Set<Long> roleIds = SecurityFrameworkUtils.getLoginUserRoleIds(); | 360 | + Set<Long> roleIds = getUserRoleIds(getLoginUserId(), singleton(CommonStatusEnum.ENABLE.getStatus())); |
| 358 | if (CollUtil.isEmpty(roleIds)) { | 361 | if (CollUtil.isEmpty(roleIds)) { |
| 359 | return false; | 362 | return false; |
| 360 | } | 363 | } |
| @@ -368,16 +371,18 @@ public class PermissionServiceImpl implements PermissionService { | @@ -368,16 +371,18 @@ public class PermissionServiceImpl implements PermissionService { | ||
| 368 | } | 371 | } |
| 369 | 372 | ||
| 370 | @Override | 373 | @Override |
| 371 | - public DeptDataPermissionRespDTO getDeptDataPermission(LoginUser loginUser) { | ||
| 372 | - // 判断是否 context 已经缓存 | ||
| 373 | - DeptDataPermissionRespDTO result = loginUser.getContext(CONTEXT_KEY, DeptDataPermissionRespDTO.class); | ||
| 374 | - if (result != null) { | 374 | + @DataPermission(enable = false) // 关闭数据权限,不然就会出现递归获取数据权限的问题 |
| 375 | + public DeptDataPermissionRespDTO getDeptDataPermission(Long userId) { | ||
| 376 | + DeptDataPermissionRespDTO result = new DeptDataPermissionRespDTO(); | ||
| 377 | + // 获得用户的角色 | ||
| 378 | + Set<Long> roleIds = getUserRoleIds(userId, singleton(CommonStatusEnum.ENABLE.getStatus())); | ||
| 379 | + if (CollUtil.isEmpty(roleIds)) { | ||
| 375 | return result; | 380 | return result; |
| 376 | } | 381 | } |
| 377 | - | ||
| 378 | - // 创建 DeptDataPermissionRespDTO 对象 | ||
| 379 | - result = new DeptDataPermissionRespDTO(); | ||
| 380 | - List<RoleDO> roles = roleService.getRolesFromCache(loginUser.getRoleIds()); | 382 | + List<RoleDO> roles = roleService.getRolesFromCache(roleIds); |
| 383 | + // 获得用户的部门编号的缓存,通过 Guava 的 Suppliers 惰性求值,即有且仅有第一次发起 DB 的查询 | ||
| 384 | + Supplier<Long> userDeptIdCache = Suppliers.memoize(() -> userService.getUser(userId).getDeptId()); | ||
| 385 | + // 遍历每个角色,计算 | ||
| 381 | for (RoleDO role : roles) { | 386 | for (RoleDO role : roles) { |
| 382 | // 为空时,跳过 | 387 | // 为空时,跳过 |
| 383 | if (role.getDataScope() == null) { | 388 | if (role.getDataScope() == null) { |
| @@ -393,20 +398,20 @@ public class PermissionServiceImpl implements PermissionService { | @@ -393,20 +398,20 @@ public class PermissionServiceImpl implements PermissionService { | ||
| 393 | CollUtil.addAll(result.getDeptIds(), role.getDataScopeDeptIds()); | 398 | CollUtil.addAll(result.getDeptIds(), role.getDataScopeDeptIds()); |
| 394 | // 自定义可见部门时,保证可以看到自己所在的部门。否则,一些场景下可能会有问题。 | 399 | // 自定义可见部门时,保证可以看到自己所在的部门。否则,一些场景下可能会有问题。 |
| 395 | // 例如说,登录时,基于 t_user 的 username 查询会可能被 dept_id 过滤掉 | 400 | // 例如说,登录时,基于 t_user 的 username 查询会可能被 dept_id 过滤掉 |
| 396 | - CollUtil.addAll(result.getDeptIds(), loginUser.getDeptId()); | 401 | + CollUtil.addAll(result.getDeptIds(), userDeptIdCache.get()); |
| 397 | continue; | 402 | continue; |
| 398 | } | 403 | } |
| 399 | // 情况三,DEPT_ONLY | 404 | // 情况三,DEPT_ONLY |
| 400 | if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_ONLY.getScope())) { | 405 | if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_ONLY.getScope())) { |
| 401 | - CollectionUtils.addIfNotNull(result.getDeptIds(), loginUser.getDeptId()); | 406 | + CollectionUtils.addIfNotNull(result.getDeptIds(), userDeptIdCache.get()); |
| 402 | continue; | 407 | continue; |
| 403 | } | 408 | } |
| 404 | // 情况四,DEPT_DEPT_AND_CHILD | 409 | // 情况四,DEPT_DEPT_AND_CHILD |
| 405 | if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_AND_CHILD.getScope())) { | 410 | if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_AND_CHILD.getScope())) { |
| 406 | - List<DeptDO> depts = deptService.getDeptsByParentIdFromCache(loginUser.getDeptId(), true); | 411 | + List<DeptDO> depts = deptService.getDeptsByParentIdFromCache(userDeptIdCache.get(), true); |
| 407 | CollUtil.addAll(result.getDeptIds(), CollectionUtils.convertList(depts, DeptDO::getId)); | 412 | CollUtil.addAll(result.getDeptIds(), CollectionUtils.convertList(depts, DeptDO::getId)); |
| 408 | - //添加本身部门id | ||
| 409 | - CollUtil.addAll(result.getDeptIds(), loginUser.getDeptId()); | 413 | + // 添加本身部门编号 |
| 414 | + CollUtil.addAll(result.getDeptIds(), userDeptIdCache.get()); | ||
| 410 | continue; | 415 | continue; |
| 411 | } | 416 | } |
| 412 | // 情况五,SELF | 417 | // 情况五,SELF |
| @@ -415,11 +420,8 @@ public class PermissionServiceImpl implements PermissionService { | @@ -415,11 +420,8 @@ public class PermissionServiceImpl implements PermissionService { | ||
| 415 | continue; | 420 | continue; |
| 416 | } | 421 | } |
| 417 | // 未知情况,error log 即可 | 422 | // 未知情况,error log 即可 |
| 418 | - log.error("[getDeptDataPermission][LoginUser({}) role({}) 无法处理]", loginUser.getId(), JsonUtils.toJsonString(result)); | 423 | + log.error("[getDeptDataPermission][LoginUser({}) role({}) 无法处理]", userId, JsonUtils.toJsonString(result)); |
| 419 | } | 424 | } |
| 420 | - | ||
| 421 | - // 添加到缓存,并返回 | ||
| 422 | - loginUser.setContext(CONTEXT_KEY, result); | ||
| 423 | return result; | 425 | return result; |
| 424 | } | 426 | } |
| 425 | 427 |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java
| @@ -81,6 +81,7 @@ public class TenantServiceImpl implements TenantService { | @@ -81,6 +81,7 @@ public class TenantServiceImpl implements TenantService { | ||
| 81 | @Getter | 81 | @Getter |
| 82 | private volatile Date maxUpdateTime; | 82 | private volatile Date maxUpdateTime; |
| 83 | 83 | ||
| 84 | + @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") | ||
| 84 | @Autowired(required = false) // 由于 yudao.tenant.enable 配置项,可以关闭多租户的功能,所以这里只能不强制注入 | 85 | @Autowired(required = false) // 由于 yudao.tenant.enable 配置项,可以关闭多租户的功能,所以这里只能不强制注入 |
| 85 | private TenantProperties tenantProperties; | 86 | private TenantProperties tenantProperties; |
| 86 | 87 |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java
| @@ -105,7 +105,6 @@ public interface AdminUserService { | @@ -105,7 +105,6 @@ public interface AdminUserService { | ||
| 105 | */ | 105 | */ |
| 106 | AdminUserDO getUserByMobile(String mobile); | 106 | AdminUserDO getUserByMobile(String mobile); |
| 107 | 107 | ||
| 108 | - | ||
| 109 | /** | 108 | /** |
| 110 | * 获得用户分页列表 | 109 | * 获得用户分页列表 |
| 111 | * | 110 | * |
| @@ -209,4 +208,13 @@ public interface AdminUserService { | @@ -209,4 +208,13 @@ public interface AdminUserService { | ||
| 209 | */ | 208 | */ |
| 210 | List<AdminUserDO> getUsersByStatus(Integer status); | 209 | List<AdminUserDO> getUsersByStatus(Integer status); |
| 211 | 210 | ||
| 211 | + /** | ||
| 212 | + * 判断密码是否匹配 | ||
| 213 | + * | ||
| 214 | + * @param rawPassword 未加密的密码 | ||
| 215 | + * @param encodedPassword 加密后的密码 | ||
| 216 | + * @return 是否匹配 | ||
| 217 | + */ | ||
| 218 | + boolean isPasswordMatch(String rawPassword, String encodedPassword); | ||
| 219 | + | ||
| 212 | } | 220 | } |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
| @@ -25,6 +25,7 @@ import cn.iocoder.yudao.module.system.service.tenant.TenantService; | @@ -25,6 +25,7 @@ import cn.iocoder.yudao.module.system.service.tenant.TenantService; | ||
| 25 | import com.google.common.annotations.VisibleForTesting; | 25 | import com.google.common.annotations.VisibleForTesting; |
| 26 | import lombok.extern.slf4j.Slf4j; | 26 | import lombok.extern.slf4j.Slf4j; |
| 27 | import org.springframework.beans.factory.annotation.Value; | 27 | import org.springframework.beans.factory.annotation.Value; |
| 28 | +import org.springframework.context.annotation.Lazy; | ||
| 28 | import org.springframework.security.crypto.password.PasswordEncoder; | 29 | import org.springframework.security.crypto.password.PasswordEncoder; |
| 29 | import org.springframework.stereotype.Service; | 30 | import org.springframework.stereotype.Service; |
| 30 | import org.springframework.transaction.annotation.Transactional; | 31 | import org.springframework.transaction.annotation.Transactional; |
| @@ -61,6 +62,7 @@ public class AdminUserServiceImpl implements AdminUserService { | @@ -61,6 +62,7 @@ public class AdminUserServiceImpl implements AdminUserService { | ||
| 61 | @Resource | 62 | @Resource |
| 62 | private PasswordEncoder passwordEncoder; | 63 | private PasswordEncoder passwordEncoder; |
| 63 | @Resource | 64 | @Resource |
| 65 | + @Lazy // 延迟,避免循环依赖报错 | ||
| 64 | private TenantService tenantService; | 66 | private TenantService tenantService; |
| 65 | 67 | ||
| 66 | @Resource | 68 | @Resource |
| @@ -146,7 +148,7 @@ public class AdminUserServiceImpl implements AdminUserService { | @@ -146,7 +148,7 @@ public class AdminUserServiceImpl implements AdminUserService { | ||
| 146 | checkOldPassword(id, reqVO.getOldPassword()); | 148 | checkOldPassword(id, reqVO.getOldPassword()); |
| 147 | // 执行更新 | 149 | // 执行更新 |
| 148 | AdminUserDO updateObj = new AdminUserDO().setId(id); | 150 | AdminUserDO updateObj = new AdminUserDO().setId(id); |
| 149 | - updateObj.setPassword(passwordEncoder.encode(reqVO.getNewPassword())); // 加密密码 | 151 | + updateObj.setPassword(encodePassword(reqVO.getNewPassword())); // 加密密码 |
| 150 | userMapper.updateById(updateObj); | 152 | userMapper.updateById(updateObj); |
| 151 | } | 153 | } |
| 152 | 154 | ||
| @@ -170,7 +172,7 @@ public class AdminUserServiceImpl implements AdminUserService { | @@ -170,7 +172,7 @@ public class AdminUserServiceImpl implements AdminUserService { | ||
| 170 | // 更新密码 | 172 | // 更新密码 |
| 171 | AdminUserDO updateObj = new AdminUserDO(); | 173 | AdminUserDO updateObj = new AdminUserDO(); |
| 172 | updateObj.setId(id); | 174 | updateObj.setId(id); |
| 173 | - updateObj.setPassword(passwordEncoder.encode(password)); // 加密密码 | 175 | + updateObj.setPassword(encodePassword(password)); // 加密密码 |
| 174 | userMapper.updateById(updateObj); | 176 | userMapper.updateById(updateObj); |
| 175 | } | 177 | } |
| 176 | 178 | ||
| @@ -203,11 +205,6 @@ public class AdminUserServiceImpl implements AdminUserService { | @@ -203,11 +205,6 @@ public class AdminUserServiceImpl implements AdminUserService { | ||
| 203 | return userMapper.selectByUsername(username); | 205 | return userMapper.selectByUsername(username); |
| 204 | } | 206 | } |
| 205 | 207 | ||
| 206 | - /** | ||
| 207 | - * 通过手机号获取用户 | ||
| 208 | - * @param mobile | ||
| 209 | - * @return | ||
| 210 | - */ | ||
| 211 | @Override | 208 | @Override |
| 212 | public AdminUserDO getUserByMobile(String mobile) { | 209 | public AdminUserDO getUserByMobile(String mobile) { |
| 213 | return userMapper.selectByMobile(mobile); | 210 | return userMapper.selectByMobile(mobile); |
| @@ -393,7 +390,7 @@ public class AdminUserServiceImpl implements AdminUserService { | @@ -393,7 +390,7 @@ public class AdminUserServiceImpl implements AdminUserService { | ||
| 393 | if (user == null) { | 390 | if (user == null) { |
| 394 | throw exception(USER_NOT_EXISTS); | 391 | throw exception(USER_NOT_EXISTS); |
| 395 | } | 392 | } |
| 396 | - if (!passwordEncoder.matches(oldPassword, user.getPassword())) { | 393 | + if (!isPasswordMatch(oldPassword, user.getPassword())) { |
| 397 | throw exception(USER_PASSWORD_FAILED); | 394 | throw exception(USER_PASSWORD_FAILED); |
| 398 | } | 395 | } |
| 399 | } | 396 | } |
| @@ -419,7 +416,7 @@ public class AdminUserServiceImpl implements AdminUserService { | @@ -419,7 +416,7 @@ public class AdminUserServiceImpl implements AdminUserService { | ||
| 419 | AdminUserDO existUser = userMapper.selectByUsername(importUser.getUsername()); | 416 | AdminUserDO existUser = userMapper.selectByUsername(importUser.getUsername()); |
| 420 | if (existUser == null) { | 417 | if (existUser == null) { |
| 421 | userMapper.insert(UserConvert.INSTANCE.convert(importUser) | 418 | userMapper.insert(UserConvert.INSTANCE.convert(importUser) |
| 422 | - .setPassword(passwordEncoder.encode(userInitPassword))); // 设置默认密码 | 419 | + .setPassword(encodePassword(userInitPassword))); // 设置默认密码 |
| 423 | respVO.getCreateUsernames().add(importUser.getUsername()); | 420 | respVO.getCreateUsernames().add(importUser.getUsername()); |
| 424 | return; | 421 | return; |
| 425 | } | 422 | } |
| @@ -441,4 +438,19 @@ public class AdminUserServiceImpl implements AdminUserService { | @@ -441,4 +438,19 @@ public class AdminUserServiceImpl implements AdminUserService { | ||
| 441 | return userMapper.selectListByStatus(status); | 438 | return userMapper.selectListByStatus(status); |
| 442 | } | 439 | } |
| 443 | 440 | ||
| 441 | + @Override | ||
| 442 | + public boolean isPasswordMatch(String rawPassword, String encodedPassword) { | ||
| 443 | + return passwordEncoder.matches(rawPassword, encodedPassword); | ||
| 444 | + } | ||
| 445 | + | ||
| 446 | + /** | ||
| 447 | + * 对密码进行加密 | ||
| 448 | + * | ||
| 449 | + * @param password 密码 | ||
| 450 | + * @return 加密后的密码 | ||
| 451 | + */ | ||
| 452 | + private String encodePassword(String password) { | ||
| 453 | + return passwordEncoder.encode(password); | ||
| 454 | + } | ||
| 455 | + | ||
| 444 | } | 456 | } |
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/AuthServiceImplTest.java
| 1 | package cn.iocoder.yudao.module.system.service.auth; | 1 | package cn.iocoder.yudao.module.system.service.auth; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 3 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 4 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | 4 | +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; |
| 5 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | 5 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
| 6 | import cn.iocoder.yudao.framework.test.core.util.AssertUtils; | 6 | import cn.iocoder.yudao.framework.test.core.util.AssertUtils; |
| 7 | import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; | 7 | import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi; |
| 8 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.AuthLoginReqVO; | 8 | import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.AuthLoginReqVO; |
| 9 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.auth.AuthLoginRespVO; | ||
| 10 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2AccessTokenDO; | ||
| 9 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | 11 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; |
| 10 | import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; | 12 | import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; |
| 11 | import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; | 13 | import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; |
| 12 | import cn.iocoder.yudao.module.system.service.common.CaptchaService; | 14 | import cn.iocoder.yudao.module.system.service.common.CaptchaService; |
| 13 | -import cn.iocoder.yudao.module.system.service.dept.PostService; | ||
| 14 | import cn.iocoder.yudao.module.system.service.logger.LoginLogService; | 15 | import cn.iocoder.yudao.module.system.service.logger.LoginLogService; |
| 15 | -import cn.iocoder.yudao.module.system.service.permission.PermissionService; | ||
| 16 | import cn.iocoder.yudao.module.system.service.social.SocialUserService; | 16 | import cn.iocoder.yudao.module.system.service.social.SocialUserService; |
| 17 | import cn.iocoder.yudao.module.system.service.user.AdminUserService; | 17 | import cn.iocoder.yudao.module.system.service.user.AdminUserService; |
| 18 | import org.junit.jupiter.api.BeforeEach; | 18 | import org.junit.jupiter.api.BeforeEach; |
| 19 | import org.junit.jupiter.api.Test; | 19 | import org.junit.jupiter.api.Test; |
| 20 | import org.springframework.boot.test.mock.mockito.MockBean; | 20 | import org.springframework.boot.test.mock.mockito.MockBean; |
| 21 | import org.springframework.context.annotation.Import; | 21 | import org.springframework.context.annotation.Import; |
| 22 | -import org.springframework.security.authentication.AuthenticationManager; | ||
| 23 | -import org.springframework.security.authentication.BadCredentialsException; | ||
| 24 | -import org.springframework.security.authentication.DisabledException; | ||
| 25 | -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
| 26 | -import org.springframework.security.core.Authentication; | ||
| 27 | -import org.springframework.security.core.AuthenticationException; | ||
| 28 | -import org.springframework.security.core.userdetails.UsernameNotFoundException; | ||
| 29 | 22 | ||
| 30 | import javax.annotation.Resource; | 23 | import javax.annotation.Resource; |
| 31 | import javax.validation.Validator; | 24 | import javax.validation.Validator; |
| 32 | -import java.util.Set; | ||
| 33 | 25 | ||
| 26 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||
| 34 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | 27 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; |
| 35 | -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | 28 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
| 29 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; | ||
| 36 | import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | 30 | import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; |
| 37 | -import static java.util.Collections.singleton; | ||
| 38 | -import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| 39 | -import static org.junit.jupiter.api.Assertions.assertThrows; | ||
| 40 | import static org.mockito.ArgumentMatchers.eq; | 31 | import static org.mockito.ArgumentMatchers.eq; |
| 41 | import static org.mockito.Mockito.*; | 32 | import static org.mockito.Mockito.*; |
| 42 | 33 | ||
| @@ -49,23 +40,15 @@ public class AuthServiceImplTest extends BaseDbUnitTest { | @@ -49,23 +40,15 @@ public class AuthServiceImplTest extends BaseDbUnitTest { | ||
| 49 | @MockBean | 40 | @MockBean |
| 50 | private AdminUserService userService; | 41 | private AdminUserService userService; |
| 51 | @MockBean | 42 | @MockBean |
| 52 | - private PermissionService permissionService; | ||
| 53 | - @MockBean | ||
| 54 | - private AuthenticationManager authenticationManager; | ||
| 55 | - @MockBean | ||
| 56 | - private Authentication authentication; | ||
| 57 | - @MockBean | ||
| 58 | private CaptchaService captchaService; | 43 | private CaptchaService captchaService; |
| 59 | @MockBean | 44 | @MockBean |
| 60 | private LoginLogService loginLogService; | 45 | private LoginLogService loginLogService; |
| 61 | @MockBean | 46 | @MockBean |
| 62 | - private UserSessionService userSessionService; | ||
| 63 | - @MockBean | ||
| 64 | private SocialUserService socialService; | 47 | private SocialUserService socialService; |
| 65 | @MockBean | 48 | @MockBean |
| 66 | - private PostService postService; | ||
| 67 | - @MockBean | ||
| 68 | private SmsCodeApi smsCodeApi; | 49 | private SmsCodeApi smsCodeApi; |
| 50 | + @MockBean | ||
| 51 | + private OAuth2TokenService oauth2TokenService; | ||
| 69 | 52 | ||
| 70 | @MockBean | 53 | @MockBean |
| 71 | private Validator validator; | 54 | private Validator validator; |
| @@ -76,216 +59,184 @@ public class AuthServiceImplTest extends BaseDbUnitTest { | @@ -76,216 +59,184 @@ public class AuthServiceImplTest extends BaseDbUnitTest { | ||
| 76 | } | 59 | } |
| 77 | 60 | ||
| 78 | @Test | 61 | @Test |
| 79 | - public void testLoadUserByUsername_success() { | 62 | + public void testLogin0_success() { |
| 80 | // 准备参数 | 63 | // 准备参数 |
| 81 | String username = randomString(); | 64 | String username = randomString(); |
| 82 | - // mock 方法 | ||
| 83 | - AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username)); | 65 | + String password = randomString(); |
| 66 | + // mock user 数据 | ||
| 67 | + AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username) | ||
| 68 | + .setPassword(password).setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||
| 84 | when(userService.getUserByUsername(eq(username))).thenReturn(user); | 69 | when(userService.getUserByUsername(eq(username))).thenReturn(user); |
| 70 | + // mock password 匹配 | ||
| 71 | + when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true); | ||
| 85 | 72 | ||
| 86 | // 调用 | 73 | // 调用 |
| 87 | - LoginUser loginUser = (LoginUser) authService.loadUserByUsername(username); | 74 | + AdminUserDO loginUser = authService.login0(username, password); |
| 88 | // 校验 | 75 | // 校验 |
| 89 | - AssertUtils.assertPojoEquals(user, loginUser, "updateTime"); | 76 | + assertPojoEquals(user, loginUser); |
| 90 | } | 77 | } |
| 91 | 78 | ||
| 92 | @Test | 79 | @Test |
| 93 | - public void testLoadUserByUsername_userNotFound() { | 80 | + public void testLogin0_userNotFound() { |
| 94 | // 准备参数 | 81 | // 准备参数 |
| 95 | String username = randomString(); | 82 | String username = randomString(); |
| 96 | - // mock 方法 | 83 | + String password = randomString(); |
| 97 | 84 | ||
| 98 | // 调用, 并断言异常 | 85 | // 调用, 并断言异常 |
| 99 | - assertThrows(UsernameNotFoundException.class, // 抛出 UsernameNotFoundException 异常 | ||
| 100 | - () -> authService.loadUserByUsername(username), | ||
| 101 | - username); // 异常提示为 username | ||
| 102 | - } | ||
| 103 | - | ||
| 104 | - @Test | ||
| 105 | - public void testMockLogin_success() { | ||
| 106 | - // 准备参数 | ||
| 107 | - Long userId = randomLongId(); | ||
| 108 | - // mock 方法 01 | ||
| 109 | - AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(userId)); | ||
| 110 | - when(userService.getUser(eq(userId))).thenReturn(user); | ||
| 111 | - // mock 方法 02 | ||
| 112 | - Set<Long> roleIds = randomSet(Long.class); | ||
| 113 | - when(permissionService.getUserRoleIds(eq(userId), eq(singleton(CommonStatusEnum.ENABLE.getStatus())))) | ||
| 114 | - .thenReturn(roleIds); | ||
| 115 | - | ||
| 116 | - // 调用 | ||
| 117 | - LoginUser loginUser = authService.mockLogin(userId); | ||
| 118 | - // 断言 | ||
| 119 | - AssertUtils.assertPojoEquals(user, loginUser, "updateTime"); | ||
| 120 | - assertEquals(roleIds, loginUser.getRoleIds()); | 86 | + AssertUtils.assertServiceException(() -> authService.login0(username, password), |
| 87 | + AUTH_LOGIN_BAD_CREDENTIALS); | ||
| 88 | + verify(loginLogService).createLoginLog( | ||
| 89 | + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) | ||
| 90 | + && o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult()) | ||
| 91 | + && o.getUserId() == null) | ||
| 92 | + ); | ||
| 121 | } | 93 | } |
| 122 | 94 | ||
| 123 | @Test | 95 | @Test |
| 124 | - public void testMockLogin_userNotFound() { | 96 | + public void testLogin0_badCredentials() { |
| 125 | // 准备参数 | 97 | // 准备参数 |
| 126 | - Long userId = randomLongId(); | ||
| 127 | - // mock 方法 | 98 | + String username = randomString(); |
| 99 | + String password = randomString(); | ||
| 100 | + // mock user 数据 | ||
| 101 | + AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username) | ||
| 102 | + .setPassword(password).setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||
| 103 | + when(userService.getUserByUsername(eq(username))).thenReturn(user); | ||
| 128 | 104 | ||
| 129 | // 调用, 并断言异常 | 105 | // 调用, 并断言异常 |
| 130 | - assertThrows(UsernameNotFoundException.class, // 抛出 UsernameNotFoundException 异常 | ||
| 131 | - () -> authService.mockLogin(userId), | ||
| 132 | - String.valueOf(userId)); // 异常提示为 userId | 106 | + AssertUtils.assertServiceException(() -> authService.login0(username, password), |
| 107 | + AUTH_LOGIN_BAD_CREDENTIALS); | ||
| 108 | + verify(loginLogService).createLoginLog( | ||
| 109 | + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) | ||
| 110 | + && o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult()) | ||
| 111 | + && o.getUserId().equals(user.getId())) | ||
| 112 | + ); | ||
| 133 | } | 113 | } |
| 134 | 114 | ||
| 135 | @Test | 115 | @Test |
| 136 | - public void testLogin_captchaNotFound() { | 116 | + public void testLogin0_userDisabled() { |
| 137 | // 准备参数 | 117 | // 准备参数 |
| 138 | - AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); | ||
| 139 | - String userIp = randomString(); | ||
| 140 | - String userAgent = randomString(); | 118 | + String username = randomString(); |
| 119 | + String password = randomString(); | ||
| 120 | + // mock user 数据 | ||
| 121 | + AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setUsername(username) | ||
| 122 | + .setPassword(password).setStatus(CommonStatusEnum.DISABLE.getStatus())); | ||
| 123 | + when(userService.getUserByUsername(eq(username))).thenReturn(user); | ||
| 124 | + // mock password 匹配 | ||
| 125 | + when(userService.isPasswordMatch(eq(password), eq(user.getPassword()))).thenReturn(true); | ||
| 141 | 126 | ||
| 142 | // 调用, 并断言异常 | 127 | // 调用, 并断言异常 |
| 143 | - assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_CAPTCHA_NOT_FOUND); | ||
| 144 | - // 校验调用参数 | ||
| 145 | - verify(loginLogService, times(1)).createLoginLog( | ||
| 146 | - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) | ||
| 147 | - && o.getResult().equals(LoginResultEnum.CAPTCHA_NOT_FOUND.getResult())) | 128 | + AssertUtils.assertServiceException(() -> authService.login0(username, password), |
| 129 | + AUTH_LOGIN_USER_DISABLED); | ||
| 130 | + verify(loginLogService).createLoginLog( | ||
| 131 | + argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) | ||
| 132 | + && o.getResult().equals(LoginResultEnum.USER_DISABLED.getResult()) | ||
| 133 | + && o.getUserId().equals(user.getId())) | ||
| 148 | ); | 134 | ); |
| 149 | } | 135 | } |
| 150 | 136 | ||
| 151 | @Test | 137 | @Test |
| 152 | - public void testLogin_captchaCodeError() { | 138 | + public void testCaptcha_success() { |
| 153 | // 准备参数 | 139 | // 准备参数 |
| 154 | - String userIp = randomString(); | ||
| 155 | - String userAgent = randomString(); | ||
| 156 | AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); | 140 | AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); |
| 157 | 141 | ||
| 158 | - // mock 验证码不正确 | ||
| 159 | - String code = randomString(); | ||
| 160 | - when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(code); | 142 | + // mock 验证码正确 |
| 143 | + when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode()); | ||
| 161 | 144 | ||
| 162 | - // 调用, 并断言异常 | ||
| 163 | - assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_CAPTCHA_CODE_ERROR); | ||
| 164 | - // 校验调用参数 | ||
| 165 | - verify(loginLogService, times(1)).createLoginLog( | ||
| 166 | - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) | ||
| 167 | - && o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult())) | ||
| 168 | - ); | 145 | + // 调用 |
| 146 | + authService.verifyCaptcha(reqVO); | ||
| 147 | + // 断言 | ||
| 148 | + verify(captchaService).deleteCaptchaCode(reqVO.getUuid()); | ||
| 169 | } | 149 | } |
| 170 | 150 | ||
| 171 | @Test | 151 | @Test |
| 172 | - public void testLogin_badCredentials() { | 152 | + public void testCaptcha_notFound() { |
| 173 | // 准备参数 | 153 | // 准备参数 |
| 174 | - String userIp = randomString(); | ||
| 175 | - String userAgent = randomString(); | ||
| 176 | AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); | 154 | AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); |
| 177 | - // mock 验证码正确 | ||
| 178 | - when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode()); | ||
| 179 | - // mock 抛出异常 | ||
| 180 | - when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword()))) | ||
| 181 | - .thenThrow(new BadCredentialsException("测试账号或密码不正确")); | ||
| 182 | 155 | ||
| 183 | // 调用, 并断言异常 | 156 | // 调用, 并断言异常 |
| 184 | - assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_BAD_CREDENTIALS); | 157 | + assertServiceException(() -> authService.verifyCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_NOT_FOUND); |
| 185 | // 校验调用参数 | 158 | // 校验调用参数 |
| 186 | - verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid()); | ||
| 187 | verify(loginLogService, times(1)).createLoginLog( | 159 | verify(loginLogService, times(1)).createLoginLog( |
| 188 | argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) | 160 | argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) |
| 189 | - && o.getResult().equals(LoginResultEnum.BAD_CREDENTIALS.getResult())) | 161 | + && o.getResult().equals(LoginResultEnum.CAPTCHA_NOT_FOUND.getResult())) |
| 190 | ); | 162 | ); |
| 191 | } | 163 | } |
| 192 | 164 | ||
| 193 | @Test | 165 | @Test |
| 194 | - public void testLogin_userDisabled() { | 166 | + public void testCaptcha_codeError() { |
| 195 | // 准备参数 | 167 | // 准备参数 |
| 196 | - String userIp = randomString(); | ||
| 197 | - String userAgent = randomString(); | ||
| 198 | AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); | 168 | AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); |
| 199 | 169 | ||
| 200 | - // mock 验证码正确 | ||
| 201 | - when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode()); | ||
| 202 | - // mock 抛出异常 | ||
| 203 | - when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword()))) | ||
| 204 | - .thenThrow(new DisabledException("测试用户被禁用")); | 170 | + // mock 验证码不正确 |
| 171 | + String code = randomString(); | ||
| 172 | + when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(code); | ||
| 205 | 173 | ||
| 206 | // 调用, 并断言异常 | 174 | // 调用, 并断言异常 |
| 207 | - assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_USER_DISABLED); | 175 | + assertServiceException(() -> authService.verifyCaptcha(reqVO), AUTH_LOGIN_CAPTCHA_CODE_ERROR); |
| 208 | // 校验调用参数 | 176 | // 校验调用参数 |
| 209 | - verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid()); | ||
| 210 | - verify(loginLogService, times(1)).createLoginLog( | 177 | + verify(loginLogService).createLoginLog( |
| 211 | argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) | 178 | argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) |
| 212 | - && o.getResult().equals(LoginResultEnum.USER_DISABLED.getResult())) | 179 | + && o.getResult().equals(LoginResultEnum.CAPTCHA_CODE_ERROR.getResult())) |
| 213 | ); | 180 | ); |
| 214 | } | 181 | } |
| 215 | 182 | ||
| 216 | @Test | 183 | @Test |
| 217 | - public void testLogin_unknownError() { | 184 | + public void testLogin_success() { |
| 218 | // 准备参数 | 185 | // 准备参数 |
| 219 | - String userIp = randomString(); | ||
| 220 | - String userAgent = randomString(); | ||
| 221 | - AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); | 186 | + AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class, o -> |
| 187 | + o.setUsername("test_username").setPassword("test_password")); | ||
| 188 | + | ||
| 222 | // mock 验证码正确 | 189 | // mock 验证码正确 |
| 223 | when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode()); | 190 | when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode()); |
| 224 | - // mock 抛出异常 | ||
| 225 | - when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword()))) | ||
| 226 | - .thenThrow(new AuthenticationException("测试未知异常") {}); | 191 | + // mock user 数据 |
| 192 | + AdminUserDO user = randomPojo(AdminUserDO.class, o -> o.setId(1L).setUsername("test_username") | ||
| 193 | + .setPassword("test_password").setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||
| 194 | + when(userService.getUserByUsername(eq("test_username"))).thenReturn(user); | ||
| 195 | + // mock password 匹配 | ||
| 196 | + when(userService.isPasswordMatch(eq("test_password"), eq(user.getPassword()))).thenReturn(true); | ||
| 197 | + // mock 缓存登录用户到 Redis | ||
| 198 | + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L) | ||
| 199 | + .setUserType(UserTypeEnum.ADMIN.getValue())); | ||
| 200 | + when(oauth2TokenService.createAccessToken(eq(1L), eq(UserTypeEnum.ADMIN.getValue()), eq(1L))) | ||
| 201 | + .thenReturn(accessTokenDO); | ||
| 227 | 202 | ||
| 228 | // 调用, 并断言异常 | 203 | // 调用, 并断言异常 |
| 229 | - assertServiceException(() -> authService.login(reqVO, userIp, userAgent), AUTH_LOGIN_FAIL_UNKNOWN); | 204 | + AuthLoginRespVO loginRespVO = authService.login(reqVO); |
| 205 | + assertPojoEquals(accessTokenDO, loginRespVO); | ||
| 230 | // 校验调用参数 | 206 | // 校验调用参数 |
| 231 | - verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid()); | ||
| 232 | - verify(loginLogService, times(1)).createLoginLog( | 207 | + verify(loginLogService).createLoginLog( |
| 233 | argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) | 208 | argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) |
| 234 | - && o.getResult().equals(LoginResultEnum.UNKNOWN_ERROR.getResult())) | 209 | + && o.getResult().equals(LoginResultEnum.SUCCESS.getResult()) |
| 210 | + && o.getUserId().equals(user.getId())) | ||
| 235 | ); | 211 | ); |
| 236 | } | 212 | } |
| 237 | 213 | ||
| 238 | @Test | 214 | @Test |
| 239 | - public void testLogin_success() { | 215 | + public void testLogout_success() { |
| 240 | // 准备参数 | 216 | // 准备参数 |
| 241 | - String userIp = randomString(); | ||
| 242 | - String userAgent = randomString(); | ||
| 243 | - AuthLoginReqVO reqVO = randomPojo(AuthLoginReqVO.class); | ||
| 244 | - | ||
| 245 | - // mock 验证码正确 | ||
| 246 | - when(captchaService.getCaptchaCode(reqVO.getUuid())).thenReturn(reqVO.getCode()); | ||
| 247 | - // mock authentication | ||
| 248 | - Long userId = randomLongId(); | ||
| 249 | - Set<Long> userRoleIds = randomSet(Long.class); | ||
| 250 | - LoginUser loginUser = randomPojo(LoginUser.class, o -> { | ||
| 251 | - o.setId(userId); | ||
| 252 | - o.setRoleIds(userRoleIds); | ||
| 253 | - }); | ||
| 254 | - when(authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(reqVO.getUsername(), reqVO.getPassword()))) | ||
| 255 | - .thenReturn(authentication); | ||
| 256 | - when(authentication.getPrincipal()).thenReturn(loginUser); | ||
| 257 | - // mock 获得 User 拥有的角色编号数组 | ||
| 258 | - when(permissionService.getUserRoleIds(userId, singleton(CommonStatusEnum.ENABLE.getStatus()))).thenReturn(userRoleIds); | ||
| 259 | - // mock 缓存登录用户到 Redis | ||
| 260 | String token = randomString(); | 217 | String token = randomString(); |
| 261 | - when(userSessionService.createUserSession(loginUser, userIp, userAgent)).thenReturn(token); | 218 | + // mock |
| 219 | + OAuth2AccessTokenDO accessTokenDO = randomPojo(OAuth2AccessTokenDO.class, o -> o.setUserId(1L) | ||
| 220 | + .setUserType(UserTypeEnum.ADMIN.getValue())); | ||
| 221 | + when(oauth2TokenService.removeAccessToken(eq(token))).thenReturn(accessTokenDO); | ||
| 262 | 222 | ||
| 263 | - // 调用, 并断言异常 | ||
| 264 | - String login = authService.login(reqVO, userIp, userAgent); | ||
| 265 | - assertEquals(token, login); | 223 | + // 调用 |
| 224 | + authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType()); | ||
| 266 | // 校验调用参数 | 225 | // 校验调用参数 |
| 267 | - verify(captchaService, times(1)).deleteCaptchaCode(reqVO.getUuid()); | ||
| 268 | - verify(loginLogService, times(1)).createLoginLog( | ||
| 269 | - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGIN_USERNAME.getType()) | 226 | + verify(loginLogService).createLoginLog(argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType()) |
| 270 | && o.getResult().equals(LoginResultEnum.SUCCESS.getResult())) | 227 | && o.getResult().equals(LoginResultEnum.SUCCESS.getResult())) |
| 271 | ); | 228 | ); |
| 272 | } | 229 | } |
| 273 | 230 | ||
| 274 | @Test | 231 | @Test |
| 275 | - public void testLogout_success() { | 232 | + public void testLogout_fail() { |
| 276 | // 准备参数 | 233 | // 准备参数 |
| 277 | String token = randomString(); | 234 | String token = randomString(); |
| 278 | - LoginUser loginUser = randomPojo(LoginUser.class); | ||
| 279 | - // mock | ||
| 280 | - when(userSessionService.getLoginUser(token)).thenReturn(loginUser); | 235 | + |
| 281 | // 调用 | 236 | // 调用 |
| 282 | - authService.logout(token); | 237 | + authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType()); |
| 283 | // 校验调用参数 | 238 | // 校验调用参数 |
| 284 | - verify(userSessionService, times(1)).deleteUserSession(token); | ||
| 285 | - verify(loginLogService, times(1)).createLoginLog( | ||
| 286 | - argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType()) | ||
| 287 | - && o.getResult().equals(LoginResultEnum.SUCCESS.getResult())) | ||
| 288 | - ); | 239 | + verify(loginLogService, never()).createLoginLog(any()); |
| 289 | } | 240 | } |
| 290 | 241 | ||
| 291 | } | 242 | } |
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/OAuth2ClientServiceImplTest.java
0 → 100755
| 1 | +package cn.iocoder.yudao.module.system.service.auth; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 5 | +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||
| 6 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientCreateReqVO; | ||
| 7 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientPageReqVO; | ||
| 8 | +import cn.iocoder.yudao.module.system.controller.admin.auth.vo.client.OAuth2ClientUpdateReqVO; | ||
| 9 | +import cn.iocoder.yudao.module.system.dal.dataobject.auth.OAuth2ClientDO; | ||
| 10 | +import cn.iocoder.yudao.module.system.dal.mysql.auth.OAuth2ClientMapper; | ||
| 11 | +import org.junit.jupiter.api.Disabled; | ||
| 12 | +import org.junit.jupiter.api.Test; | ||
| 13 | +import org.springframework.context.annotation.Import; | ||
| 14 | + | ||
| 15 | +import javax.annotation.Resource; | ||
| 16 | + | ||
| 17 | +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | ||
| 18 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||
| 19 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; | ||
| 20 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; | ||
| 21 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | ||
| 22 | +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.OAUTH2_CLIENT_NOT_EXISTS; | ||
| 23 | +import static org.junit.jupiter.api.Assertions.*; | ||
| 24 | + | ||
| 25 | +/** | ||
| 26 | +* {@link OAuth2ClientServiceImpl} 的单元测试类 | ||
| 27 | +* | ||
| 28 | +* @author 芋道源码 | ||
| 29 | +*/ | ||
| 30 | +@Import(OAuth2ClientServiceImpl.class) | ||
| 31 | +public class OAuth2ClientServiceImplTest extends BaseDbUnitTest { | ||
| 32 | + | ||
| 33 | + @Resource | ||
| 34 | + private OAuth2ClientServiceImpl oAuth2ClientService; | ||
| 35 | + | ||
| 36 | + @Resource | ||
| 37 | + private OAuth2ClientMapper oAuth2ClientMapper; | ||
| 38 | + | ||
| 39 | + @Test | ||
| 40 | + public void testCreateOAuth2Client_success() { | ||
| 41 | + // 准备参数 | ||
| 42 | + OAuth2ClientCreateReqVO reqVO = randomPojo(OAuth2ClientCreateReqVO.class); | ||
| 43 | + | ||
| 44 | + // 调用 | ||
| 45 | + Long oauth2ClientId = oAuth2ClientService.createOAuth2Client(reqVO); | ||
| 46 | + // 断言 | ||
| 47 | + assertNotNull(oauth2ClientId); | ||
| 48 | + // 校验记录的属性是否正确 | ||
| 49 | + OAuth2ClientDO oAuth2Client = oAuth2ClientMapper.selectById(oauth2ClientId); | ||
| 50 | + assertPojoEquals(reqVO, oAuth2Client); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + @Test | ||
| 54 | + public void testUpdateOAuth2Client_success() { | ||
| 55 | + // mock 数据 | ||
| 56 | + OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class); | ||
| 57 | + oAuth2ClientMapper.insert(dbOAuth2Client);// @Sql: 先插入出一条存在的数据 | ||
| 58 | + // 准备参数 | ||
| 59 | + OAuth2ClientUpdateReqVO reqVO = randomPojo(OAuth2ClientUpdateReqVO.class, o -> { | ||
| 60 | + o.setId(dbOAuth2Client.getId()); // 设置更新的 ID | ||
| 61 | + }); | ||
| 62 | + | ||
| 63 | + // 调用 | ||
| 64 | + oAuth2ClientService.updateOAuth2Client(reqVO); | ||
| 65 | + // 校验是否更新正确 | ||
| 66 | + OAuth2ClientDO oAuth2Client = oAuth2ClientMapper.selectById(reqVO.getId()); // 获取最新的 | ||
| 67 | + assertPojoEquals(reqVO, oAuth2Client); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + @Test | ||
| 71 | + public void testUpdateOAuth2Client_notExists() { | ||
| 72 | + // 准备参数 | ||
| 73 | + OAuth2ClientUpdateReqVO reqVO = randomPojo(OAuth2ClientUpdateReqVO.class); | ||
| 74 | + | ||
| 75 | + // 调用, 并断言异常 | ||
| 76 | + assertServiceException(() -> oAuth2ClientService.updateOAuth2Client(reqVO), OAUTH2_CLIENT_NOT_EXISTS); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + @Test | ||
| 80 | + public void testDeleteOAuth2Client_success() { | ||
| 81 | + // mock 数据 | ||
| 82 | + OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class); | ||
| 83 | + oAuth2ClientMapper.insert(dbOAuth2Client);// @Sql: 先插入出一条存在的数据 | ||
| 84 | + // 准备参数 | ||
| 85 | + Long id = dbOAuth2Client.getId(); | ||
| 86 | + | ||
| 87 | + // 调用 | ||
| 88 | + oAuth2ClientService.deleteOAuth2Client(id); | ||
| 89 | + // 校验数据不存在了 | ||
| 90 | + assertNull(oAuth2ClientMapper.selectById(id)); | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + @Test | ||
| 94 | + public void testDeleteOAuth2Client_notExists() { | ||
| 95 | + // 准备参数 | ||
| 96 | + Long id = randomLongId(); | ||
| 97 | + | ||
| 98 | + // 调用, 并断言异常 | ||
| 99 | + assertServiceException(() -> oAuth2ClientService.deleteOAuth2Client(id), OAUTH2_CLIENT_NOT_EXISTS); | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + @Test | ||
| 103 | + @Disabled | ||
| 104 | + public void testGetOAuth2ClientPage() { | ||
| 105 | + // mock 数据 | ||
| 106 | + OAuth2ClientDO dbOAuth2Client = randomPojo(OAuth2ClientDO.class, o -> { // 等会查询到 | ||
| 107 | + o.setName("潜龙"); | ||
| 108 | + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||
| 109 | + }); | ||
| 110 | + oAuth2ClientMapper.insert(dbOAuth2Client); | ||
| 111 | + // 测试 name 不匹配 | ||
| 112 | + oAuth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setName("凤凰"))); | ||
| 113 | + // 测试 status 不匹配 | ||
| 114 | + oAuth2ClientMapper.insert(cloneIgnoreId(dbOAuth2Client, o -> o.setStatus(CommonStatusEnum.ENABLE.getStatus()))); | ||
| 115 | + // 准备参数 | ||
| 116 | + OAuth2ClientPageReqVO reqVO = new OAuth2ClientPageReqVO(); | ||
| 117 | + reqVO.setName("long"); | ||
| 118 | + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||
| 119 | + | ||
| 120 | + // 调用 | ||
| 121 | + PageResult<OAuth2ClientDO> pageResult = oAuth2ClientService.getOAuth2ClientPage(reqVO); | ||
| 122 | + // 断言 | ||
| 123 | + assertEquals(1, pageResult.getTotal()); | ||
| 124 | + assertEquals(1, pageResult.getList().size()); | ||
| 125 | + assertPojoEquals(dbOAuth2Client, pageResult.getList().get(0)); | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | +} |
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/auth/UserSessionServiceImplTest.java deleted
| 1 | -package cn.iocoder.yudao.module.system.service.auth; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 4 | -import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; | ||
| 5 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 6 | -import cn.iocoder.yudao.framework.common.util.date.DateUtils; | ||
| 7 | -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; | ||
| 8 | -import cn.iocoder.yudao.framework.security.config.SecurityProperties; | ||
| 9 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 10 | -import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; | ||
| 11 | -import cn.iocoder.yudao.module.system.controller.admin.auth.vo.session.UserSessionPageReqVO; | ||
| 12 | -import cn.iocoder.yudao.module.system.dal.dataobject.auth.UserSessionDO; | ||
| 13 | -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | ||
| 14 | -import cn.iocoder.yudao.module.system.dal.mysql.auth.UserSessionMapper; | ||
| 15 | -import cn.iocoder.yudao.module.system.dal.redis.auth.LoginUserRedisDAO; | ||
| 16 | -import cn.iocoder.yudao.module.system.enums.common.SexEnum; | ||
| 17 | -import cn.iocoder.yudao.module.system.enums.logger.LoginLogTypeEnum; | ||
| 18 | -import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum; | ||
| 19 | -import cn.iocoder.yudao.module.system.service.logger.LoginLogService; | ||
| 20 | -import cn.iocoder.yudao.module.system.service.user.AdminUserService; | ||
| 21 | -import org.junit.jupiter.api.BeforeEach; | ||
| 22 | -import org.junit.jupiter.api.Test; | ||
| 23 | -import org.springframework.boot.test.mock.mockito.MockBean; | ||
| 24 | -import org.springframework.context.annotation.Import; | ||
| 25 | - | ||
| 26 | -import javax.annotation.Resource; | ||
| 27 | -import java.time.Duration; | ||
| 28 | -import java.util.Calendar; | ||
| 29 | - | ||
| 30 | -import static cn.hutool.core.util.RandomUtil.randomEle; | ||
| 31 | -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.addTime; | ||
| 32 | -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | ||
| 33 | -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | ||
| 34 | -import static java.util.Collections.singletonList; | ||
| 35 | -import static org.junit.jupiter.api.Assertions.*; | ||
| 36 | -import static org.mockito.ArgumentMatchers.argThat; | ||
| 37 | -import static org.mockito.ArgumentMatchers.eq; | ||
| 38 | -import static org.mockito.Mockito.verify; | ||
| 39 | -import static org.mockito.Mockito.when; | ||
| 40 | - | ||
| 41 | -/** | ||
| 42 | - * {@link UserSessionServiceImpl} 的单元测试 | ||
| 43 | - * | ||
| 44 | - * @author Lyon | ||
| 45 | - */ | ||
| 46 | -@Import({UserSessionServiceImpl.class, LoginUserRedisDAO.class}) | ||
| 47 | -public class UserSessionServiceImplTest extends BaseDbAndRedisUnitTest { | ||
| 48 | - | ||
| 49 | - @Resource | ||
| 50 | - private UserSessionServiceImpl userSessionService; | ||
| 51 | - | ||
| 52 | - @Resource | ||
| 53 | - private UserSessionMapper userSessionMapper; | ||
| 54 | - | ||
| 55 | - @MockBean | ||
| 56 | - private AdminUserService userService; | ||
| 57 | - @MockBean | ||
| 58 | - private LoginLogService loginLogService; | ||
| 59 | - @Resource | ||
| 60 | - private LoginUserRedisDAO loginUserRedisDAO; | ||
| 61 | - | ||
| 62 | - @MockBean | ||
| 63 | - private SecurityProperties securityProperties; | ||
| 64 | - | ||
| 65 | - @BeforeEach | ||
| 66 | - public void setUp() { | ||
| 67 | - when(securityProperties.getSessionTimeout()).thenReturn(Duration.ofDays(1L)); | ||
| 68 | - } | ||
| 69 | - | ||
| 70 | - @Test | ||
| 71 | - public void testGetUserSessionPage_success() { | ||
| 72 | - // mock 数据 | ||
| 73 | - AdminUserDO dbUser = randomPojo(AdminUserDO.class, o -> { | ||
| 74 | - o.setSex(randomEle(SexEnum.values()).getSex()); | ||
| 75 | - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); | ||
| 76 | - }); | ||
| 77 | - when(userService.getUsersByUsername(eq(dbUser.getUsername()))).thenReturn(singletonList(dbUser)); | ||
| 78 | - // 插入可被查询到的数据 | ||
| 79 | - String userIp = randomString(); | ||
| 80 | - UserSessionDO dbSession = randomPojo(UserSessionDO.class, o -> { | ||
| 81 | - o.setUserId(dbUser.getId()); | ||
| 82 | - o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | ||
| 83 | - o.setUserIp(userIp); | ||
| 84 | - }); | ||
| 85 | - userSessionMapper.insert(dbSession); | ||
| 86 | - // 测试 username 不匹配 | ||
| 87 | - userSessionMapper.insert(ObjectUtils.cloneIgnoreId(dbSession, o -> o.setUserId(123456L))); | ||
| 88 | - // 测试 userIp 不匹配 | ||
| 89 | - userSessionMapper.insert(ObjectUtils.cloneIgnoreId(dbSession, o -> o.setUserIp("testUserIp"))); | ||
| 90 | - // 准备参数 | ||
| 91 | - UserSessionPageReqVO reqVO = new UserSessionPageReqVO(); | ||
| 92 | - reqVO.setUsername(dbUser.getUsername()); | ||
| 93 | - reqVO.setUserIp(userIp); | ||
| 94 | - | ||
| 95 | - // 调用 | ||
| 96 | - PageResult<UserSessionDO> pageResult = userSessionService.getUserSessionPage(reqVO); | ||
| 97 | - // 断言 | ||
| 98 | - assertEquals(1, pageResult.getTotal()); | ||
| 99 | - assertEquals(1, pageResult.getList().size()); | ||
| 100 | - assertPojoEquals(dbSession, pageResult.getList().get(0)); | ||
| 101 | - } | ||
| 102 | - | ||
| 103 | - @Test | ||
| 104 | - public void testClearSessionTimeout_none() { | ||
| 105 | - // mock db 数据 | ||
| 106 | - UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> { | ||
| 107 | - o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | ||
| 108 | - o.setSessionTimeout(addTime(Duration.ofDays(1))); | ||
| 109 | - }); | ||
| 110 | - userSessionMapper.insert(userSession); | ||
| 111 | - | ||
| 112 | - // 调用 | ||
| 113 | - long count = userSessionService.deleteTimeoutSession(); | ||
| 114 | - // 断言 | ||
| 115 | - assertEquals(0, count); | ||
| 116 | - assertPojoEquals(userSession, userSessionMapper.selectById(userSession.getId())); // 未删除 | ||
| 117 | - } | ||
| 118 | - | ||
| 119 | - @Test // Redis 还存在的情况 | ||
| 120 | - public void testClearSessionTimeout_exists() { | ||
| 121 | - // mock db 数据 | ||
| 122 | - UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> { | ||
| 123 | - o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | ||
| 124 | - o.setSessionTimeout(DateUtils.addDate(Calendar.DAY_OF_YEAR, -1)); | ||
| 125 | - }); | ||
| 126 | - userSessionMapper.insert(userSession); | ||
| 127 | - // mock redis 数据 | ||
| 128 | - loginUserRedisDAO.set(userSession.getToken(), new LoginUser()); | ||
| 129 | - | ||
| 130 | - // 调用 | ||
| 131 | - long count = userSessionService.deleteTimeoutSession(); | ||
| 132 | - // 断言 | ||
| 133 | - assertEquals(0, count); | ||
| 134 | - assertPojoEquals(userSession, userSessionMapper.selectById(userSession.getId())); // 未删除 | ||
| 135 | - } | ||
| 136 | - | ||
| 137 | - @Test | ||
| 138 | - public void testClearSessionTimeout_success() { | ||
| 139 | - // mock db 数据 | ||
| 140 | - UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> { | ||
| 141 | - o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | ||
| 142 | - o.setSessionTimeout(DateUtils.addDate(Calendar.DAY_OF_YEAR, -1)); | ||
| 143 | - }); | ||
| 144 | - userSessionMapper.insert(userSession); | ||
| 145 | - | ||
| 146 | - // 清空超时数据 | ||
| 147 | - long count = userSessionService.deleteTimeoutSession(); | ||
| 148 | - // 校验 | ||
| 149 | - assertEquals(1, count); | ||
| 150 | - assertNull(userSessionMapper.selectById(userSession.getId())); // 已删除 | ||
| 151 | - verify(loginLogService).createLoginLog(argThat(loginLog -> { | ||
| 152 | - assertPojoEquals(userSession, loginLog); | ||
| 153 | - assertEquals(LoginLogTypeEnum.LOGOUT_TIMEOUT.getType(), loginLog.getLogType()); | ||
| 154 | - assertEquals(LoginResultEnum.SUCCESS.getResult(), loginLog.getResult()); | ||
| 155 | - return true; | ||
| 156 | - })); | ||
| 157 | - } | ||
| 158 | - | ||
| 159 | - @Test | ||
| 160 | - public void testCreateUserSession_success() { | ||
| 161 | - // 准备参数 | ||
| 162 | - String userIp = randomString(); | ||
| 163 | - String userAgent = randomString(); | ||
| 164 | - LoginUser loginUser = randomPojo(LoginUser.class, o -> { | ||
| 165 | - o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | ||
| 166 | - o.setTenantId(0L); // 租户设置为 0,因为暂未启用多租户组件 | ||
| 167 | - }); | ||
| 168 | - | ||
| 169 | - // 调用 | ||
| 170 | - String token = userSessionService.createUserSession(loginUser, userIp, userAgent); | ||
| 171 | - // 校验 UserSessionDO 记录 | ||
| 172 | - UserSessionDO userSessionDO = userSessionMapper.selectOne(UserSessionDO::getToken, token); | ||
| 173 | - assertPojoEquals(loginUser, userSessionDO, "id", "updateTime"); | ||
| 174 | - assertEquals(token, userSessionDO.getToken()); | ||
| 175 | - assertEquals(userIp, userSessionDO.getUserIp()); | ||
| 176 | - assertEquals(userAgent, userSessionDO.getUserAgent()); | ||
| 177 | - // 校验 LoginUser 缓存 | ||
| 178 | - LoginUser redisLoginUser = loginUserRedisDAO.get(token); | ||
| 179 | - assertPojoEquals(loginUser, redisLoginUser, "username", "password"); | ||
| 180 | - } | ||
| 181 | - | ||
| 182 | - @Test | ||
| 183 | - public void testCreateRefreshUserSession() { | ||
| 184 | - // 准备参数 | ||
| 185 | - String token = randomString(); | ||
| 186 | - | ||
| 187 | - // mock redis 数据 | ||
| 188 | - LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setUserType(randomEle(UserTypeEnum.values()).getValue())); | ||
| 189 | - loginUserRedisDAO.set(token, loginUser); | ||
| 190 | - // mock db 数据 | ||
| 191 | - UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> { | ||
| 192 | - o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | ||
| 193 | - o.setToken(token); | ||
| 194 | - }); | ||
| 195 | - userSessionMapper.insert(userSession); | ||
| 196 | - | ||
| 197 | - // 调用 | ||
| 198 | - userSessionService.refreshUserSession(token, loginUser); | ||
| 199 | - // 校验 LoginUser 缓存 | ||
| 200 | - LoginUser redisLoginUser = loginUserRedisDAO.get(token); | ||
| 201 | - assertPojoEquals(redisLoginUser, loginUser, "username", "password"); | ||
| 202 | - // 校验 UserSessionDO 记录 | ||
| 203 | - UserSessionDO updateDO = userSessionMapper.selectOne(UserSessionDO::getToken, token); | ||
| 204 | - assertEquals(updateDO.getUsername(), loginUser.getUsername()); | ||
| 205 | - assertNotNull(userSession.getUpdateTime()); | ||
| 206 | - assertNotNull(userSession.getSessionTimeout()); | ||
| 207 | - } | ||
| 208 | - | ||
| 209 | - @Test | ||
| 210 | - public void testDeleteUserSession_Token() { | ||
| 211 | - // 准备参数 | ||
| 212 | - String token = randomString(); | ||
| 213 | - | ||
| 214 | - // mock redis 数据 | ||
| 215 | - loginUserRedisDAO.set(token, new LoginUser()); | ||
| 216 | - // mock db 数据 | ||
| 217 | - UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> { | ||
| 218 | - o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | ||
| 219 | - o.setToken(token); | ||
| 220 | - }); | ||
| 221 | - userSessionMapper.insert(userSession); | ||
| 222 | - | ||
| 223 | - // 调用 | ||
| 224 | - userSessionService.deleteUserSession(token); | ||
| 225 | - // 校验数据不存在了 | ||
| 226 | - assertNull(loginUserRedisDAO.get(token)); | ||
| 227 | - assertNull(userSessionMapper.selectOne(UserSessionDO::getToken, token)); | ||
| 228 | - } | ||
| 229 | - | ||
| 230 | - @Test | ||
| 231 | - public void testDeleteUserSession_Id() { | ||
| 232 | - // mock db 数据 | ||
| 233 | - UserSessionDO userSession = randomPojo(UserSessionDO.class, o -> { | ||
| 234 | - o.setUserType(randomEle(UserTypeEnum.values()).getValue()); | ||
| 235 | - }); | ||
| 236 | - userSessionMapper.insert(userSession); | ||
| 237 | - // mock redis 数据 | ||
| 238 | - loginUserRedisDAO.set(userSession.getToken(), new LoginUser()); | ||
| 239 | - | ||
| 240 | - // 准备参数 | ||
| 241 | - Long id = userSession.getId(); | ||
| 242 | - | ||
| 243 | - // 调用 | ||
| 244 | - userSessionService.deleteUserSession(id); | ||
| 245 | - // 校验数据不存在了 | ||
| 246 | - assertNull(loginUserRedisDAO.get(userSession.getToken())); | ||
| 247 | - assertNull(userSessionMapper.selectById(id)); | ||
| 248 | - } | ||
| 249 | - | ||
| 250 | -} |
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java
| 1 | package cn.iocoder.yudao.module.system.service.permission; | 1 | package cn.iocoder.yudao.module.system.service.permission; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | ||
| 5 | +import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO; | ||
| 6 | +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | ||
| 4 | import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; | 7 | import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; |
| 5 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; | 8 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; |
| 6 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO; | 9 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO; |
| 7 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO; | 10 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO; |
| 11 | +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; | ||
| 8 | import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuBatchInsertMapper; | 12 | import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuBatchInsertMapper; |
| 9 | import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper; | 13 | import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper; |
| 10 | import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleBatchInsertMapper; | 14 | import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleBatchInsertMapper; |
| 11 | import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper; | 15 | import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper; |
| 16 | +import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; | ||
| 12 | import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer; | 17 | import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer; |
| 13 | import cn.iocoder.yudao.module.system.service.dept.DeptService; | 18 | import cn.iocoder.yudao.module.system.service.dept.DeptService; |
| 14 | -import cn.iocoder.yudao.framework.datapermission.core.dept.service.dto.DeptDataPermissionRespDTO; | ||
| 15 | -import cn.iocoder.yudao.framework.security.core.LoginUser; | ||
| 16 | -import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; | ||
| 17 | -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; | 19 | +import cn.iocoder.yudao.module.system.service.user.AdminUserService; |
| 18 | import org.junit.jupiter.api.Test; | 20 | import org.junit.jupiter.api.Test; |
| 19 | import org.springframework.boot.test.mock.mockito.MockBean; | 21 | import org.springframework.boot.test.mock.mockito.MockBean; |
| 20 | import org.springframework.context.annotation.Import; | 22 | import org.springframework.context.annotation.Import; |
| @@ -25,10 +27,10 @@ import java.util.List; | @@ -25,10 +27,10 @@ import java.util.List; | ||
| 25 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | 27 | import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; |
| 26 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; | 28 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; |
| 27 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; | 29 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
| 30 | +import static java.util.Collections.singleton; | ||
| 28 | import static java.util.Collections.singletonList; | 31 | import static java.util.Collections.singletonList; |
| 29 | import static org.junit.jupiter.api.Assertions.*; | 32 | import static org.junit.jupiter.api.Assertions.*; |
| 30 | import static org.mockito.ArgumentMatchers.eq; | 33 | import static org.mockito.ArgumentMatchers.eq; |
| 31 | -import static org.mockito.ArgumentMatchers.same; | ||
| 32 | import static org.mockito.Mockito.verify; | 34 | import static org.mockito.Mockito.verify; |
| 33 | import static org.mockito.Mockito.when; | 35 | import static org.mockito.Mockito.when; |
| 34 | 36 | ||
| @@ -55,6 +57,9 @@ public class PermissionServiceTest extends BaseDbUnitTest { | @@ -55,6 +57,9 @@ public class PermissionServiceTest extends BaseDbUnitTest { | ||
| 55 | @MockBean | 57 | @MockBean |
| 56 | private DeptService deptService; | 58 | private DeptService deptService; |
| 57 | @MockBean | 59 | @MockBean |
| 60 | + private AdminUserService userService; | ||
| 61 | + | ||
| 62 | + @MockBean | ||
| 58 | private PermissionProducer permissionProducer; | 63 | private PermissionProducer permissionProducer; |
| 59 | 64 | ||
| 60 | @Test | 65 | @Test |
| @@ -124,112 +129,119 @@ public class PermissionServiceTest extends BaseDbUnitTest { | @@ -124,112 +129,119 @@ public class PermissionServiceTest extends BaseDbUnitTest { | ||
| 124 | assertPojoEquals(dbUserRoles.get(0), userRoleDO02); | 129 | assertPojoEquals(dbUserRoles.get(0), userRoleDO02); |
| 125 | } | 130 | } |
| 126 | 131 | ||
| 127 | - @Test // 测试从 context 获取的场景 | ||
| 128 | - public void testGetDeptDataPermission_fromContext() { | ||
| 129 | - // 准备参数 | ||
| 130 | - LoginUser loginUser = randomPojo(LoginUser.class); | ||
| 131 | - // mock 方法 | ||
| 132 | - DeptDataPermissionRespDTO respDTO = new DeptDataPermissionRespDTO(); | ||
| 133 | - loginUser.setContext(PermissionServiceImpl.CONTEXT_KEY, respDTO); | ||
| 134 | - | ||
| 135 | - // 调用 | ||
| 136 | - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(loginUser); | ||
| 137 | - // 断言 | ||
| 138 | - assertSame(respDTO, result); | ||
| 139 | - } | ||
| 140 | - | ||
| 141 | @Test | 132 | @Test |
| 142 | public void testGetDeptDataPermission_All() { | 133 | public void testGetDeptDataPermission_All() { |
| 143 | // 准备参数 | 134 | // 准备参数 |
| 144 | - LoginUser loginUser = randomPojo(LoginUser.class); | ||
| 145 | - // mock 方法 | ||
| 146 | - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.ALL.getScope())); | ||
| 147 | - when(roleService.getRolesFromCache(same(loginUser.getRoleIds()))).thenReturn(singletonList(roleDO)); | 135 | + Long userId = 1L; |
| 136 | + // mock 用户的角色编号 | ||
| 137 | + userRoleMapper.insert(new UserRoleDO().setUserId(userId).setRoleId(2L)); | ||
| 138 | + // mock 获得用户的角色 | ||
| 139 | + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.ALL.getScope()) | ||
| 140 | + .setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||
| 141 | + when(roleService.getRolesFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO)); | ||
| 142 | + when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO); | ||
| 148 | 143 | ||
| 149 | // 调用 | 144 | // 调用 |
| 150 | - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(loginUser); | 145 | + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); |
| 151 | // 断言 | 146 | // 断言 |
| 152 | assertTrue(result.getAll()); | 147 | assertTrue(result.getAll()); |
| 153 | assertFalse(result.getSelf()); | 148 | assertFalse(result.getSelf()); |
| 154 | assertTrue(CollUtil.isEmpty(result.getDeptIds())); | 149 | assertTrue(CollUtil.isEmpty(result.getDeptIds())); |
| 155 | - assertSame(result, loginUser.getContext(PermissionServiceImpl.CONTEXT_KEY, DeptDataPermissionRespDTO.class)); | ||
| 156 | } | 150 | } |
| 157 | 151 | ||
| 158 | @Test | 152 | @Test |
| 159 | public void testGetDeptDataPermission_DeptCustom() { | 153 | public void testGetDeptDataPermission_DeptCustom() { |
| 160 | // 准备参数 | 154 | // 准备参数 |
| 161 | - LoginUser loginUser = randomPojo(LoginUser.class); | ||
| 162 | - // mock 方法 | ||
| 163 | - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope())); | ||
| 164 | - when(roleService.getRolesFromCache(same(loginUser.getRoleIds()))).thenReturn(singletonList(roleDO)); | 155 | + Long userId = 1L; |
| 156 | + // mock 用户的角色编号 | ||
| 157 | + userRoleMapper.insert(new UserRoleDO().setUserId(userId).setRoleId(2L)); | ||
| 158 | + // mock 获得用户的角色 | ||
| 159 | + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope()) | ||
| 160 | + .setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||
| 161 | + when(roleService.getRolesFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO)); | ||
| 162 | + when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO); | ||
| 163 | + // mock 部门的返回 | ||
| 164 | + when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), null, null); // 最后返回 null 的目的,看看会不会重复调用 | ||
| 165 | 165 | ||
| 166 | // 调用 | 166 | // 调用 |
| 167 | - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(loginUser); | 167 | + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(1L); |
| 168 | // 断言 | 168 | // 断言 |
| 169 | assertFalse(result.getAll()); | 169 | assertFalse(result.getAll()); |
| 170 | assertFalse(result.getSelf()); | 170 | assertFalse(result.getSelf()); |
| 171 | assertEquals(roleDO.getDataScopeDeptIds().size() + 1, result.getDeptIds().size()); | 171 | assertEquals(roleDO.getDataScopeDeptIds().size() + 1, result.getDeptIds().size()); |
| 172 | assertTrue(CollUtil.containsAll(result.getDeptIds(), roleDO.getDataScopeDeptIds())); | 172 | assertTrue(CollUtil.containsAll(result.getDeptIds(), roleDO.getDataScopeDeptIds())); |
| 173 | - assertTrue(CollUtil.contains(result.getDeptIds(), loginUser.getDeptId())); | ||
| 174 | - assertSame(result, loginUser.getContext(PermissionServiceImpl.CONTEXT_KEY, DeptDataPermissionRespDTO.class)); | 173 | + assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); |
| 175 | } | 174 | } |
| 176 | 175 | ||
| 177 | @Test | 176 | @Test |
| 178 | public void testGetDeptDataPermission_DeptOnly() { | 177 | public void testGetDeptDataPermission_DeptOnly() { |
| 179 | // 准备参数 | 178 | // 准备参数 |
| 180 | - LoginUser loginUser = randomPojo(LoginUser.class); | ||
| 181 | - // mock 方法 | ||
| 182 | - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_ONLY.getScope())); | ||
| 183 | - when(roleService.getRolesFromCache(same(loginUser.getRoleIds()))).thenReturn(singletonList(roleDO)); | 179 | + Long userId = 1L; |
| 180 | + // mock 用户的角色编号 | ||
| 181 | + userRoleMapper.insert(new UserRoleDO().setUserId(userId).setRoleId(2L)); | ||
| 182 | + // mock 获得用户的角色 | ||
| 183 | + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_ONLY.getScope()) | ||
| 184 | + .setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||
| 185 | + when(roleService.getRolesFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO)); | ||
| 186 | + when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO); | ||
| 187 | + // mock 部门的返回 | ||
| 188 | + when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), null, null); // 最后返回 null 的目的,看看会不会重复调用 | ||
| 184 | 189 | ||
| 185 | // 调用 | 190 | // 调用 |
| 186 | - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(loginUser); | 191 | + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(1L); |
| 187 | // 断言 | 192 | // 断言 |
| 188 | assertFalse(result.getAll()); | 193 | assertFalse(result.getAll()); |
| 189 | assertFalse(result.getSelf()); | 194 | assertFalse(result.getSelf()); |
| 190 | assertEquals(1, result.getDeptIds().size()); | 195 | assertEquals(1, result.getDeptIds().size()); |
| 191 | - assertTrue(CollUtil.contains(result.getDeptIds(), loginUser.getDeptId())); | ||
| 192 | - assertSame(result, loginUser.getContext(PermissionServiceImpl.CONTEXT_KEY, DeptDataPermissionRespDTO.class)); | 196 | + assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); |
| 193 | } | 197 | } |
| 194 | 198 | ||
| 195 | @Test | 199 | @Test |
| 196 | public void testGetDeptDataPermission_DeptAndChild() { | 200 | public void testGetDeptDataPermission_DeptAndChild() { |
| 197 | // 准备参数 | 201 | // 准备参数 |
| 198 | - LoginUser loginUser = randomPojo(LoginUser.class); | ||
| 199 | - // mock 方法(角色) | ||
| 200 | - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_AND_CHILD.getScope())); | ||
| 201 | - when(roleService.getRolesFromCache(same(loginUser.getRoleIds()))).thenReturn(singletonList(roleDO)); | 202 | + Long userId = 1L; |
| 203 | + // mock 用户的角色编号 | ||
| 204 | + userRoleMapper.insert(new UserRoleDO().setUserId(userId).setRoleId(2L)); | ||
| 205 | + // mock 获得用户的角色 | ||
| 206 | + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_AND_CHILD.getScope()) | ||
| 207 | + .setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||
| 208 | + when(roleService.getRolesFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO)); | ||
| 209 | + when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO); | ||
| 210 | + // mock 部门的返回 | ||
| 211 | + when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), null, null); // 最后返回 null 的目的,看看会不会重复调用 | ||
| 202 | // mock 方法(部门) | 212 | // mock 方法(部门) |
| 203 | DeptDO deptDO = randomPojo(DeptDO.class); | 213 | DeptDO deptDO = randomPojo(DeptDO.class); |
| 204 | - when(deptService.getDeptsByParentIdFromCache(eq(loginUser.getDeptId()), eq(true))) | 214 | + when(deptService.getDeptsByParentIdFromCache(eq(3L), eq(true))) |
| 205 | .thenReturn(singletonList(deptDO)); | 215 | .thenReturn(singletonList(deptDO)); |
| 206 | 216 | ||
| 207 | // 调用 | 217 | // 调用 |
| 208 | - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(loginUser); | 218 | + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); |
| 209 | // 断言 | 219 | // 断言 |
| 210 | assertFalse(result.getAll()); | 220 | assertFalse(result.getAll()); |
| 211 | assertFalse(result.getSelf()); | 221 | assertFalse(result.getSelf()); |
| 212 | assertEquals(2, result.getDeptIds().size()); | 222 | assertEquals(2, result.getDeptIds().size()); |
| 213 | assertTrue(CollUtil.contains(result.getDeptIds(), deptDO.getId())); | 223 | assertTrue(CollUtil.contains(result.getDeptIds(), deptDO.getId())); |
| 214 | - assertTrue(CollUtil.contains(result.getDeptIds(), loginUser.getDeptId())); | ||
| 215 | - assertSame(result, loginUser.getContext(PermissionServiceImpl.CONTEXT_KEY, DeptDataPermissionRespDTO.class)); | 224 | + assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); |
| 216 | } | 225 | } |
| 217 | 226 | ||
| 218 | @Test | 227 | @Test |
| 219 | public void testGetDeptDataPermission_Self() { | 228 | public void testGetDeptDataPermission_Self() { |
| 220 | // 准备参数 | 229 | // 准备参数 |
| 221 | - LoginUser loginUser = randomPojo(LoginUser.class); | ||
| 222 | - // mock 方法 | ||
| 223 | - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.SELF.getScope())); | ||
| 224 | - when(roleService.getRolesFromCache(same(loginUser.getRoleIds()))).thenReturn(singletonList(roleDO)); | 230 | + Long userId = 1L; |
| 231 | + // mock 用户的角色编号 | ||
| 232 | + userRoleMapper.insert(new UserRoleDO().setUserId(userId).setRoleId(2L)); | ||
| 233 | + // mock 获得用户的角色 | ||
| 234 | + RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.SELF.getScope()) | ||
| 235 | + .setStatus(CommonStatusEnum.ENABLE.getStatus())); | ||
| 236 | + when(roleService.getRolesFromCache(eq(singleton(2L)))).thenReturn(singletonList(roleDO)); | ||
| 237 | + when(roleService.getRoleFromCache(eq(2L))).thenReturn(roleDO); | ||
| 225 | 238 | ||
| 226 | // 调用 | 239 | // 调用 |
| 227 | - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(loginUser); | 240 | + DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); |
| 228 | // 断言 | 241 | // 断言 |
| 229 | assertFalse(result.getAll()); | 242 | assertFalse(result.getAll()); |
| 230 | assertTrue(result.getSelf()); | 243 | assertTrue(result.getSelf()); |
| 231 | assertTrue(CollUtil.isEmpty(result.getDeptIds())); | 244 | assertTrue(CollUtil.isEmpty(result.getDeptIds())); |
| 232 | - assertSame(result, loginUser.getContext(PermissionServiceImpl.CONTEXT_KEY, DeptDataPermissionRespDTO.class)); | ||
| 233 | } | 245 | } |
| 234 | 246 | ||
| 235 | } | 247 | } |
yudao-module-system/yudao-module-system-biz/src/test/resources/sql/clean.sql
| @@ -20,3 +20,4 @@ DELETE FROM "system_social_user_bind"; | @@ -20,3 +20,4 @@ DELETE FROM "system_social_user_bind"; | ||
| 20 | DELETE FROM "system_tenant"; | 20 | DELETE FROM "system_tenant"; |
| 21 | DELETE FROM "system_tenant_package"; | 21 | DELETE FROM "system_tenant_package"; |
| 22 | DELETE FROM "system_sensitive_word"; | 22 | DELETE FROM "system_sensitive_word"; |
| 23 | +DELETE FROM "system_oauth2_client"; |
yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql
| @@ -470,3 +470,21 @@ CREATE TABLE IF NOT EXISTS "system_sensitive_word" ( | @@ -470,3 +470,21 @@ CREATE TABLE IF NOT EXISTS "system_sensitive_word" ( | ||
| 470 | "deleted" bit NOT NULL DEFAULT FALSE, | 470 | "deleted" bit NOT NULL DEFAULT FALSE, |
| 471 | PRIMARY KEY ("id") | 471 | PRIMARY KEY ("id") |
| 472 | ) COMMENT '系统敏感词'; | 472 | ) COMMENT '系统敏感词'; |
| 473 | + | ||
| 474 | +CREATE TABLE IF NOT EXISTS "system_oauth2_client" ( | ||
| 475 | + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, | ||
| 476 | + "secret" varchar NOT NULL, | ||
| 477 | + "name" varchar NOT NULL, | ||
| 478 | + "logo" varchar NOT NULL, | ||
| 479 | + "description" varchar, | ||
| 480 | + "status" int NOT NULL, | ||
| 481 | + "access_token_validity_seconds" int NOT NULL, | ||
| 482 | + "refresh_token_validity_seconds" int NOT NULL, | ||
| 483 | + "redirect_uris" varchar NOT NULL, | ||
| 484 | + "creator" varchar DEFAULT '', | ||
| 485 | + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
| 486 | + "updater" varchar DEFAULT '', | ||
| 487 | + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | ||
| 488 | + "deleted" bit NOT NULL DEFAULT FALSE, | ||
| 489 | + PRIMARY KEY ("id") | ||
| 490 | +) COMMENT 'OAuth2 客户端表'; |
yudao-server/src/main/resources/application-dev.yaml
| @@ -173,8 +173,6 @@ wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-sta | @@ -173,8 +173,6 @@ wx: # 参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-sta | ||
| 173 | yudao: | 173 | yudao: |
| 174 | security: | 174 | security: |
| 175 | token-header: Authorization | 175 | token-header: Authorization |
| 176 | - token-timeout: 1d | ||
| 177 | - session-timeout: 30m | ||
| 178 | mock-enable: true | 176 | mock-enable: true |
| 179 | mock-secret: test | 177 | mock-secret: test |
| 180 | xss: | 178 | xss: |