Commit dd8c5a5e by tangyi

多租户

parent 47c7fba0
Version v3.0.0 (2019-6-9)
--------------------------
新功能:
* 支持多租户,增加租户中心->单位管理,表结构增加tenant_code字段
* 增加登录日志
* 增加controller层的参数校验逻辑
* 用户管理增加重置密码功能
改进:
* 调整后台管理的按钮样式
* 调整认证逻辑、token加入租户标识
* 前台、后台的网关地址可通过环境变量配置
* 方法鉴权优化
* 调整系统架构图
* 优化终端管理的授权范围和授权类型
* 统一前后台的eslint,解决启动报的语法错误问题
Bug修复:
* 修复查看错题时错题不标红的bug
* 修复swagger文档显示不了的问题
* 修复开始答题报错问题
* 修复注册失败的bug
......@@ -4,7 +4,11 @@
- 在线体验-后台:[http://www.it99.club:81](http://www.it99.club:81)
交流QQ群:996208878
交流QQ群:
<a target="_blank" href="https://jq.qq.com/?_wv=1027&k=5RKZNF2"><img border="0" src="http://pub.idqqimg.com/wpa/images/group.png" alt="Spring Cloud考试系统学习" title="Spring Cloud考试系统学习"></a>
群号:996208878
如果您觉得有帮助,请点右上角 "Star" 或者项目底部的“捐助”支持一下,谢谢!
......@@ -26,6 +30,8 @@
默认账号:
单位ID:gitee
1. 管理员:admin/123456
2. 学生:student/123456
3. 教师:teacher/123456
......@@ -59,7 +65,7 @@
## 系统架构
![image](doc/产品设计/系统架构图.png)
![image](doc/产品设计/系统架构图v3.0.jpg)
## 功能概述
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.github.tangyi</groupId>
<artifactId>common</artifactId>
<version>2.0</version>
<version>3.0.0</version>
</parent>
<artifactId>common-core</artifactId>
<name>${project.artifactId}</name>
......@@ -101,5 +101,17 @@
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>${jasypt.version}</version>
</dependency>
<!-- hibernate-validator参数校验 -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<!-- security -->
<dependency>
<groupId>com.github.tangyi</groupId>
<artifactId>common-security</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
package com.github.tangyi.common.core.cache;
import org.springframework.dao.PessimisticLockingFailureException;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Collections;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* 源码:org.springframework.data.redis.cache.DefaultRedisCacheWriter
*
* @author tangyi
* @date 2019/6/9 15:28
*/
public class CustomRedisCacheWriter implements RedisCacheWriter {
private final RedisConnectionFactory connectionFactory;
private final Duration sleepTime;
/**
* @param connectionFactory must not be {@literal null}.
*/
public CustomRedisCacheWriter(RedisConnectionFactory connectionFactory) {
this(connectionFactory, Duration.ZERO);
}
/**
* @param connectionFactory must not be {@literal null}.
* @param sleepTime sleep time between lock request attempts. Must not be {@literal null}. Use {@link Duration#ZERO}
* to disable locking.
*/
public CustomRedisCacheWriter(RedisConnectionFactory connectionFactory, Duration sleepTime) {
Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");
Assert.notNull(sleepTime, "SleepTime must not be null!");
this.connectionFactory = connectionFactory;
this.sleepTime = sleepTime;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.cache.RedisCacheWriter#put(java.lang.String, byte[], byte[], java.time.Duration)
*/
@Override
public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
Assert.notNull(name, "Name must not be null!");
Assert.notNull(key, "Key must not be null!");
Assert.notNull(value, "Value must not be null!");
execute(name, connection -> {
if (shouldExpireWithin(ttl)) {
connection.set(key, value, Expiration.from(ttl.toMillis(), TimeUnit.MILLISECONDS), RedisStringCommands.SetOption.upsert());
} else {
connection.set(key, value);
}
return "OK";
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.cache.RedisCacheWriter#get(java.lang.String, byte[])
*/
@Override
public byte[] get(String name, byte[] key) {
Assert.notNull(name, "Name must not be null!");
Assert.notNull(key, "Key must not be null!");
return execute(name, connection -> connection.get(key));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.cache.RedisCacheWriter#putIfAbsent(java.lang.String, byte[], byte[], java.time.Duration)
*/
@Override
public byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Duration ttl) {
Assert.notNull(name, "Name must not be null!");
Assert.notNull(key, "Key must not be null!");
Assert.notNull(value, "Value must not be null!");
return execute(name, connection -> {
if (isLockingCacheWriter()) {
doLock(name, connection);
}
try {
if (connection.setNX(key, value)) {
if (shouldExpireWithin(ttl)) {
connection.pExpire(key, ttl.toMillis());
}
return null;
}
return connection.get(key);
} finally {
if (isLockingCacheWriter()) {
doUnlock(name, connection);
}
}
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.cache.RedisCacheWriter#remove(java.lang.String, byte[])
*/
@Override
public void remove(String name, byte[] key) {
Assert.notNull(name, "Name must not be null!");
Assert.notNull(key, "Key must not be null!");
execute(name, connection -> connection.del(key));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.cache.RedisCacheWriter#clean(java.lang.String, byte[])
*/
@Override
public void clean(String name, byte[] pattern) {
Assert.notNull(name, "Name must not be null!");
Assert.notNull(pattern, "Pattern must not be null!");
execute(name, connection -> {
boolean wasLocked = false;
try {
if (isLockingCacheWriter()) {
doLock(name, connection);
wasLocked = true;
}
byte[][] keys = Optional.ofNullable(connection.keys(pattern)).orElse(Collections.emptySet())
.toArray(new byte[0][]);
if (keys.length > 0) {
connection.del(keys);
}
} finally {
if (wasLocked && isLockingCacheWriter()) {
doUnlock(name, connection);
}
}
return "OK";
});
}
/**
* Explicitly set a write lock on a cache.
*
* @param name the name of the cache to lock.
*/
void lock(String name) {
execute(name, connection -> doLock(name, connection));
}
/**
* Explicitly remove a write lock from a cache.
*
* @param name the name of the cache to unlock.
*/
void unlock(String name) {
executeLockFree(connection -> doUnlock(name, connection));
}
private Boolean doLock(String name, RedisConnection connection) {
return connection.setNX(createCacheLockKey(name), new byte[0]);
}
private Long doUnlock(String name, RedisConnection connection) {
return connection.del(createCacheLockKey(name));
}
boolean doCheckLock(String name, RedisConnection connection) {
return connection.exists(createCacheLockKey(name));
}
/**
* @return {@literal true} if {@link RedisCacheWriter} uses locks.
*/
private boolean isLockingCacheWriter() {
return !sleepTime.isZero() && !sleepTime.isNegative();
}
private <T> T execute(String name, Function<RedisConnection, T> callback) {
RedisConnection connection = connectionFactory.getConnection();
try {
checkAndPotentiallyWaitUntilUnlocked(name, connection);
return callback.apply(connection);
} finally {
connection.close();
}
}
private void executeLockFree(Consumer<RedisConnection> callback) {
RedisConnection connection = connectionFactory.getConnection();
try {
callback.accept(connection);
} finally {
connection.close();
}
}
private void checkAndPotentiallyWaitUntilUnlocked(String name, RedisConnection connection) {
if (!isLockingCacheWriter()) {
return;
}
try {
while (doCheckLock(name, connection)) {
Thread.sleep(sleepTime.toMillis());
}
} catch (InterruptedException ex) {
// Re-interrupt current thread, to allow other participants to react.
Thread.currentThread().interrupt();
throw new PessimisticLockingFailureException(String.format("Interrupted while waiting to unlock cache %s", name),
ex);
}
}
private static boolean shouldExpireWithin(@Nullable Duration ttl) {
return ttl != null && !ttl.isZero() && !ttl.isNegative();
}
private static byte[] createCacheLockKey(String name) {
return (name + "~lock").getBytes(StandardCharsets.UTF_8);
}
}
package com.github.tangyi.common.core.cache;
import com.github.tangyi.common.core.utils.SysUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import java.util.Map;
/**
* 扩展CacheManager支持多租户
*
* @author tangyi
* @date 2019/6/9 15:12
*/
@Slf4j
public class MultitenantCacheManager extends RedisCacheManager {
public MultitenantCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
Map<String, RedisCacheConfiguration> initialCacheConfigurations, boolean allowInFlightCacheCreation) {
super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations, allowInFlightCacheCreation);
}
/**
* 扩展cache name,加上tenantCode作为前缀
*
* @param name name
* @return Cache
*/
@Override
public Cache getCache(String name) {
name = SysUtil.getTenantCode() + ":" + name;
log.info("cache name: {}", name);
return super.getCache(name);
}
}
package com.github.tangyi.common.core.config;
import com.github.tangyi.common.core.cache.CustomRedisCacheWriter;
import com.github.tangyi.common.core.cache.MultitenantCacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* redis配置
*
......@@ -32,6 +40,19 @@ public class RedisConfig {
return redisTemplate;
}
/**
* 多租户cacheManager
*
* @return RedisCacheManager
*/
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheWriter redisCacheWriter = new CustomRedisCacheWriter(connectionFactory);
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
Map<String, RedisCacheConfiguration> initialCacheConfigurations = new LinkedHashMap<>();
return new MultitenantCacheManager(redisCacheWriter, redisCacheConfiguration, initialCacheConfigurations, true);
}
@Bean
public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
return redisTemplate.opsForHash();
......
......@@ -16,7 +16,12 @@ public class CommonConstant {
/**
* 默认租户编号
*/
public static final String TENANT_CODE = "GITHUB";
public static final String DEFAULT_TENANT_CODE = "gitee";
/**
* 租户编号
*/
public static final String TENANT_CODE = "tenantCode";
/**
* JSON 资源
......@@ -148,5 +153,10 @@ public class CommonConstant {
*/
public static final String AUTHORIZATION_BEARER = "Bearer ";
/**
* 密码类型
*/
public static final String GRANT_TYPE_PASSWORD = "password";
}
package com.github.tangyi.common.core.exceptions;
/**
* token非法异常
*
* @author tangyi
* @date 2019/5/27 17:52
*/
public class InvalidAccessTokenException extends CommonException {
private static final long serialVersionUID = -7285211528095468156L;
public InvalidAccessTokenException() {
}
public InvalidAccessTokenException(String msg) {
super(msg);
}
}
package com.github.tangyi.common.core.exceptions;
/**
* 租户不存在异常
*
* @author tangyi
* @date 2019/5/26 10:14
*/
public class TenantNotFoundException extends CommonException {
private static final long serialVersionUID = -7285211528095468156L;
public TenantNotFoundException() {
}
public TenantNotFoundException(String msg) {
super(msg);
}
}
......@@ -3,6 +3,8 @@ package com.github.tangyi.common.core.model;
import com.github.tangyi.common.core.persistence.BaseEntity;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* 日志
*
......@@ -15,11 +17,13 @@ public class Log extends BaseEntity<Log> {
/**
* 日志类型
*/
@NotBlank(message = "日志类型不能为空")
private String type;
/**
* 日志标题
*/
@NotBlank(message = "日志标题不能为空")
private String title;
/**
......
......@@ -3,6 +3,8 @@ package com.github.tangyi.common.core.model;
import com.github.tangyi.common.core.persistence.BaseEntity;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* 路由信息
*
......@@ -15,6 +17,7 @@ public class Route extends BaseEntity<Route> {
/**
* 路由ID
*/
@NotBlank(message = "路由id不能为空")
private String routeId;
/**
......@@ -35,6 +38,7 @@ public class Route extends BaseEntity<Route> {
/**
* URI
*/
@NotBlank(message = "路由URI不能为空")
private String uri;
/**
......@@ -45,5 +49,6 @@ public class Route extends BaseEntity<Route> {
/**
* 启用禁用
*/
@NotBlank(message = "路由状态不能为空")
private String status;
}
......@@ -3,6 +3,7 @@ package com.github.tangyi.common.core.persistence;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.utils.DateUtils;
import com.github.tangyi.common.core.utils.IdGen;
import com.github.tangyi.common.core.utils.SysUtil;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang.StringUtils;
......@@ -95,7 +96,7 @@ public class BaseEntity<T> implements Serializable {
* @param applicationCode 系统编号
*/
public void setCommonValue(String userCode, String applicationCode) {
setCommonValue(userCode, applicationCode, "");
setCommonValue(userCode, applicationCode, SysUtil.getTenantCode());
}
/**
......
package com.github.tangyi.common.core.persistence;
import com.github.tangyi.common.core.persistence.BaseEntity;
import lombok.Data;
import java.util.ArrayList;
......
package com.github.tangyi.common.core.tenant;
/**
* ThreadLocal保存租户code
*
* @author tangyi
* @date 2019/5/28 20:54
*/
public class TenantContextHolder {
private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();
public static void setTenantCode(String tenantCode) {
CONTEXT.set(tenantCode);
}
public static String getTenantCode() {
return CONTEXT.get();
}
public static void clear() {
CONTEXT.remove();
}
}
package com.github.tangyi.common.core.utils;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.tenant.TenantContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
/**
* 系统工具类
......@@ -8,20 +22,23 @@ import com.github.tangyi.common.core.constant.CommonConstant;
* @author tangyi
* @date 2018-09-13 20:50
*/
@Slf4j
public class SysUtil {
private static final String KEY_USER = "user";
private static final String BASIC_ = "Basic ";
/**
* 从thread local 获取用户名
* 获取当前登录的用户名
*
* @return 用户名
* @return String
* @author tangyi
* @date 2019/03/17 11:46
*/
public static String getUser() {
return null;
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails)
return ((UserDetails) principal).getUsername();
if (principal instanceof Principal)
return ((Principal) principal).getName();
return String.valueOf(principal);
}
/**
......@@ -39,6 +56,41 @@ public class SysUtil {
* @return String
*/
public static String getTenantCode() {
return CommonConstant.TENANT_CODE;
String tenantCode = TenantContextHolder.getTenantCode();
if (StringUtils.isBlank(tenantCode))
tenantCode = getCurrentUserTenantCode();
if (StringUtils.isBlank(tenantCode))
tenantCode = CommonConstant.DEFAULT_TENANT_CODE;
log.debug("租户code:{}", tenantCode);
return tenantCode;
}
/**
* 获取当前登录的租户code
*
* @return String
*/
private static String getCurrentUserTenantCode() {
String tenantCode = "";
try {
ResourceServerTokenServices resourceServerTokenServices = SpringContextHolder.getApplicationContext().getBean(ResourceServerTokenServices.class);
Object details = SecurityContextHolder.getContext().getAuthentication().getDetails();
if (details instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails oAuth2AuthenticationDetails = (OAuth2AuthenticationDetails) details;
OAuth2AccessToken oAuth2AccessToken = resourceServerTokenServices.readAccessToken(oAuth2AuthenticationDetails.getTokenValue());
Object tenantObj = oAuth2AccessToken.getAdditionalInformation().get(CommonConstant.TENANT_CODE);
tenantCode = tenantObj == null ? "" : tenantObj.toString();
} else if (details instanceof WebAuthenticationDetails) {
// 未认证
Object requestObj = RequestContextHolder.getRequestAttributes();
if (requestObj != null) {
HttpServletRequest request = ((ServletRequestAttributes) requestObj).getRequest();
tenantCode = request.getParameter(CommonConstant.TENANT_CODE);
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return tenantCode;
}
}
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.github.tangyi</groupId>
<artifactId>common</artifactId>
<version>2.0</version>
<version>3.0.0</version>
</parent>
<artifactId>common-feign</artifactId>
<name>${project.artifactId}</name>
......
......@@ -3,10 +3,10 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.github.tangyi</groupId>
<artifactId>common</artifactId>
<version>2.0</version>
</parent>
<groupId>com.github.tangyi</groupId>
<artifactId>common</artifactId>
<version>3.0.0</version>
</parent>
<artifactId>common-log</artifactId>
<name>${project.artifactId}</name>
<description>日志公共依赖</description>
......
......@@ -5,7 +5,6 @@ import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.log.event.LogEvent;
import com.github.tangyi.common.log.utils.LogUtil;
import com.github.tangyi.common.security.utils.SecurityUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
......@@ -35,7 +34,7 @@ public class LogAspect {
Object obj = point.proceed();
Long endTime = System.currentTimeMillis();
logVo.setTime(String.valueOf(endTime - startTime));
logVo.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
logVo.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode());
SpringContextHolder.publishEvent(new LogEvent(logVo));
return obj;
}
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.github.tangyi</groupId>
<artifactId>common</artifactId>
<version>2.0</version>
</parent>
<artifactId>common-security</artifactId>
<name>${project.artifactId}</name>
<description>security公共依赖</description>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.github.tangyi</groupId>
<artifactId>common</artifactId>
<version>3.0.0</version>
</parent>
<artifactId>common-security</artifactId>
<name>${project.artifactId}</name>
<description>security公共依赖</description>
<dependencies>
<!-- 单点登录 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
</dependencies>
<dependencies>
<!-- 单点登录 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
</dependencies>
</project>
......@@ -12,31 +12,16 @@ public class SecurityConstant {
public static final String BASE_ROLE = "role_user";
/**
* 管理员角色
* 超级管理员角色
*/
public static final String ROLE_ADMIN = "role_admin";
/**
* 老师角色
*/
public static final String ROLE_TEACHER = "role_teacher";
/**
* token
*/
public static final String TOKEN_USER_DETAIL = "token-user-detail";
/**
* 默认生成图形验证码过期时间
*/
public static final int DEFAULT_IMAGE_EXPIRE = 60;
/**
* oauth 客户端信息
*/
public static final String CLIENT_DETAILS_KEY = "exam_oauth:client:details";
/**
* 正常状态
*/
public static final String NORMAL = "0";
......
package com.github.tangyi.common.security.core;
import com.github.tangyi.common.security.constant.SecurityConstant;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
......@@ -12,6 +13,7 @@ import java.util.Set;
* @author tangyi
* @date 2019/3/17 14:37
*/
@Data
public class UserDetailsImpl implements UserDetails {
private static final long serialVersionUID = -6509897037222767090L;
......@@ -36,7 +38,13 @@ public class UserDetailsImpl implements UserDetails {
*/
private String status;
public UserDetailsImpl(String username, String password, String status, Set<GrantedAuthority> authorities) {
/**
* 租户标识
*/
private String tenantCode;
public UserDetailsImpl(String tenantCode, String username, String password, String status, Set<GrantedAuthority> authorities) {
this.tenantCode = tenantCode;
this.authorities = authorities;
this.username = username;
this.password = password;
......
package com.github.tangyi.common.security.utils;
import com.github.tangyi.common.security.core.UserDetailsImpl;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
......@@ -15,6 +16,18 @@ import java.security.Principal;
public class SecurityUtil {
/**
* 获取当前用户的租户标识
*
* @return String
* @author tangyi
* @date 2019/05/25 14:19
*/
public static String getCurrentUserTenantCode() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return principal instanceof UserDetails ? ((UserDetailsImpl) principal).getTenantCode() : "";
}
/**
* 获取当前登录的用户名
*
* @return String
......@@ -28,7 +41,6 @@ public class SecurityUtil {
if (principal instanceof Principal)
return ((Principal) principal).getName();
return String.valueOf(principal);
}
/**
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.github.tangyi</groupId>
<artifactId>spring-microservice-exam</artifactId>
<version>2.0</version>
</parent>
<artifactId>common</artifactId>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>公共依赖</description>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.github.tangyi</groupId>
<artifactId>spring-microservice-exam</artifactId>
<version>3.0.0</version>
</parent>
<artifactId>common</artifactId>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>公共依赖</description>
<modules>
<module>common-core</module>
<module>common-security</module>
<module>common-feign</module>
<module>common-log</module>
</modules>
<modules>
<module>common-core</module>
<module>common-security</module>
<module>common-feign</module>
<module>common-log</module>
</modules>
</project>
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.github.tangyi</groupId>
<artifactId>spring-microservice-exam</artifactId>
<version>2.0</version>
<version>3.0.0</version>
</parent>
<artifactId>config-service</artifactId>
<name>${project.artifactId}</name>
......
......@@ -83,6 +83,7 @@ ignore:
- /actuator/**
- /hystrix.sender
- /v1/user/findUserByUsername/**
- /v1/tenant/findTenantByTenantCode/**
- /v1/user/checkExist/**
- /v1/menu/findMenuByRole/**
- /v1/menu/findAllMenu
......
......@@ -48,15 +48,15 @@ security:
jwt:
key-uri: http://${AUTH_SERVICE_HOST:localhost}:${AUTH_SERVICE_PORT:8000}/api/auth/oauth/token_key
key-value: |
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjxSawA+NUNmhB2ctiVnt
YH41WCNoD5STW2iPm5AIsKvm6X67lr6A88qOMRwx9oySdZnUnJ+8L3QJ51fkwuDe
ix5w9yA3f/7LUPWZU8M/7Oi+2nda05JMgU999TUlhTGsp9SPiBqq/iwuqMxU8xKu
F8bpTJTOpzrxH4e5BM6J/UJcOwDZLY6/3zP5w+tbhTvxjc995G4NtUyS4owE1MHe
lj8IJepknjePrE6nXD6ecdL401hstMY838UOOFiCnM8NpiBuNI0nY0qCbb9mhQZ+
7gP3jjM+Ft7R+MFTuEHWQ5UN8qHAPIT9UlLcu9IXdk6YwTsqNavwaTLUcP/ih9HB
6wIDAQAB
-----END PUBLIC KEY-----
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjxSawA+NUNmhB2ctiVnt
YH41WCNoD5STW2iPm5AIsKvm6X67lr6A88qOMRwx9oySdZnUnJ+8L3QJ51fkwuDe
ix5w9yA3f/7LUPWZU8M/7Oi+2nda05JMgU999TUlhTGsp9SPiBqq/iwuqMxU8xKu
F8bpTJTOpzrxH4e5BM6J/UJcOwDZLY6/3zP5w+tbhTvxjc995G4NtUyS4owE1MHe
lj8IJepknjePrE6nXD6ecdL401hstMY838UOOFiCnM8NpiBuNI0nY0qCbb9mhQZ+
7gP3jjM+Ft7R+MFTuEHWQ5UN8qHAPIT9UlLcu9IXdk6YwTsqNavwaTLUcP/ih9HB
6wIDAQAB
-----END PUBLIC KEY-----
# mybatis配置
mybatis:
......@@ -68,10 +68,10 @@ mybatis:
lazy-loading-enabled: true
mapper-locations: classpath:mapper/*.xml
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
feign:
hystrix:
......@@ -107,6 +107,7 @@ ignore:
- /actuator/**
- /hystrix.sender
- /v1/user/findUserByUsername/**
- /v1/tenant/findTenantByTenantCode/**
- /v1/menu/findMenuByRole/**
- /v1/menu/findAllMenu
- /v1/user/checkExist/**
......@@ -133,4 +134,4 @@ cluster:
logging:
level:
root: info
com.github.tangyi: debug
\ No newline at end of file
com.github.tangyi: debug
......@@ -54,15 +54,15 @@ security:
jwt:
key-uri: http://${AUTH_SERVICE_HOST:localhost}:${AUTH_SERVICE_PORT:8000}/api/auth/oauth/token_key
key-value: |
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjxSawA+NUNmhB2ctiVnt
YH41WCNoD5STW2iPm5AIsKvm6X67lr6A88qOMRwx9oySdZnUnJ+8L3QJ51fkwuDe
ix5w9yA3f/7LUPWZU8M/7Oi+2nda05JMgU999TUlhTGsp9SPiBqq/iwuqMxU8xKu
F8bpTJTOpzrxH4e5BM6J/UJcOwDZLY6/3zP5w+tbhTvxjc995G4NtUyS4owE1MHe
lj8IJepknjePrE6nXD6ecdL401hstMY838UOOFiCnM8NpiBuNI0nY0qCbb9mhQZ+
7gP3jjM+Ft7R+MFTuEHWQ5UN8qHAPIT9UlLcu9IXdk6YwTsqNavwaTLUcP/ih9HB
6wIDAQAB
-----END PUBLIC KEY-----
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjxSawA+NUNmhB2ctiVnt
YH41WCNoD5STW2iPm5AIsKvm6X67lr6A88qOMRwx9oySdZnUnJ+8L3QJ51fkwuDe
ix5w9yA3f/7LUPWZU8M/7Oi+2nda05JMgU999TUlhTGsp9SPiBqq/iwuqMxU8xKu
F8bpTJTOpzrxH4e5BM6J/UJcOwDZLY6/3zP5w+tbhTvxjc995G4NtUyS4owE1MHe
lj8IJepknjePrE6nXD6ecdL401hstMY838UOOFiCnM8NpiBuNI0nY0qCbb9mhQZ+
7gP3jjM+Ft7R+MFTuEHWQ5UN8qHAPIT9UlLcu9IXdk6YwTsqNavwaTLUcP/ih9HB
6wIDAQAB
-----END PUBLIC KEY-----
# mybatis配置
mybatis:
......@@ -74,10 +74,10 @@ mybatis:
lazy-loading-enabled: true
mapper-locations: classpath:mapper/*.xml
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
# ===================================================================
# 分布式文件系统FDFS配置
......@@ -89,7 +89,7 @@ fdfs:
width: 150
height: 150
tracker-list: #TrackerList参数,支持多个
- ${FDFS_HOST:192.168.0.95}:${FDFS_PORT:22122}
- ${FDFS_HOST:182.254.233.125}:${FDFS_PORT:22122}
# 系统配置
sys:
......@@ -131,6 +131,7 @@ ignore:
- /actuator/**
- /hystrix.sender
- /v1/user/findUserByUsername/**
- /v1/tenant/findTenantByTenantCode/**
- /v1/menu/findMenuByRole/**
- /v1/menu/findAllMenu
- /v1/user/register
......@@ -159,4 +160,4 @@ cluster:
logging:
level:
root: info
com.github.tangyi: debug
\ No newline at end of file
com.github.tangyi: debug
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="false">
<springProperty scop="context" name="spring.application.name" source="spring.application.name" defaultValue="spring-microservice-exam"/>
<property name="log.path" value="logs/${spring.application.name}" />
<springProperty scop="context" name="spring.application.name" source="spring.application.name"
defaultValue="spring-microservice-exam"/>
<property name="log.path" value="logs/${spring.application.name}"/>
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
......@@ -40,8 +41,8 @@
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="debug" />
<appender-ref ref="error" />
<appender-ref ref="console"/>
<appender-ref ref="debug"/>
<appender-ref ref="error"/>
</root>
</configuration>
\ No newline at end of file
</configuration>
......@@ -11,7 +11,7 @@
Target Server Version : 50617
File Encoding : 65001
Date: 22/05/2019 22:21:34
Date: 09/06/2019 16:45:45
*/
SET NAMES utf8mb4;
......@@ -48,6 +48,6 @@ CREATE TABLE `oauth_client_details` (
-- ----------------------------
-- Records of oauth_client_details
-- ----------------------------
INSERT INTO `oauth_client_details` VALUES ('471037a0c1be4df99b40e3f84141cc56', 'web_app', NULL, 'spring-microservice-exam-secret', '$2a$10$IWLD8r7e0rKMW.ZhGsGCUO./MZUNDKudIswSToa9puXJwty.h59.u', 'read,write', 'password,authorization_code,refresh_token,implicit', NULL, NULL, '3600', '21600', NULL, NULL, 'admin', '2019-03-30 23:43:07', 'admin', '2019-05-22 22:16:18', '0', 'EXAM', NULL);
INSERT INTO `oauth_client_details` VALUES ('471037a0c1be4df99b40e3f84141cc56', 'web_app', NULL, 'spring-microservice-exam-secret', '$2a$10$IWLD8r7e0rKMW.ZhGsGCUO./MZUNDKudIswSToa9puXJwty.h59.u', 'read,write', 'password,authorization_code,refresh_token,implicit', NULL, NULL, '3600', '21600', NULL, NULL, 'admin', '2019-03-30 23:43:07', 'admin', '2019-06-07 21:48:31', '0', 'EXAM', NULL);
SET FOREIGN_KEY_CHECKS = 1;
......@@ -5,7 +5,7 @@ DOCKERHOME=/spring-microservice-exam
# 镜像名称前缀、标签
BASE_IMAGE_NAME=registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam
BSEE_IMAGE_TAG=2.0
BSEE_IMAGE_TAG=3.0.0
# 各服务的镜像名称
CONFIG_SERVICE=$BASE_IMAGE_NAME/config-service:$BSEE_IMAGE_TAG
......
doc/images/image_ui.png

105 KB | W: | H:

doc/images/image_ui.png

113 KB | W: | H:

doc/images/image_ui.png
doc/images/image_ui.png
doc/images/image_ui.png
doc/images/image_ui.png
  • 2-up
  • Swipe
  • Onion skin
doc/images/image_ui_attachment.png

43 KB | W: | H:

doc/images/image_ui_attachment.png

63.3 KB | W: | H:

doc/images/image_ui_attachment.png
doc/images/image_ui_attachment.png
doc/images/image_ui_attachment.png
doc/images/image_ui_attachment.png
  • 2-up
  • Swipe
  • Onion skin
doc/images/image_ui_exam.png

61.8 KB | W: | H:

doc/images/image_ui_exam.png

62.9 KB | W: | H:

doc/images/image_ui_exam.png
doc/images/image_ui_exam.png
doc/images/image_ui_exam.png
doc/images/image_ui_exam.png
  • 2-up
  • Swipe
  • Onion skin
doc/images/image_ui_exam_subject.png

157 KB | W: | H:

doc/images/image_ui_exam_subject.png

120 KB | W: | H:

doc/images/image_ui_exam_subject.png
doc/images/image_ui_exam_subject.png
doc/images/image_ui_exam_subject.png
doc/images/image_ui_exam_subject.png
  • 2-up
  • Swipe
  • Onion skin
doc/images/image_ui_menu.png

123 KB | W: | H:

doc/images/image_ui_menu.png

99.8 KB | W: | H:

doc/images/image_ui_menu.png
doc/images/image_ui_menu.png
doc/images/image_ui_menu.png
doc/images/image_ui_menu.png
  • 2-up
  • Swipe
  • Onion skin
doc/images/image_ui_subject.png

101 KB | W: | H:

doc/images/image_ui_subject.png

128 KB | W: | H:

doc/images/image_ui_subject.png
doc/images/image_ui_subject.png
doc/images/image_ui_subject.png
doc/images/image_ui_subject.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -13,7 +13,7 @@
- consul
- fastDfs(推荐基于docker安装,安装步骤网上很多)
开发工具:`IntelliJ IDEA``WebStorm``IntelliJ IDEA`需要安装插件:`lombok`
开发工具:`IntelliJ IDEA``IntelliJ IDEA`需要安装插件:`lombok`
#### 项目下载
......@@ -79,7 +79,7 @@ fastDfs的IP和端口号:
![image](images/deploy/npm_run.png)
`WebStorm`导入项目:
`IntelliJ IDEA`导入项目:
![image](images/deploy/import_exam_ui.png)
......
......@@ -70,7 +70,7 @@ services:
# 配置中心
# ---------------------------
config-service:
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/config-service:2.0
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/config-service:3.0.0
container_name: config-service
env_file: docker-compose.env # 从文件中获取配置
restart: always
......
......@@ -4,7 +4,7 @@ services:
# api网关
# ---------------------------
gateway-service:
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/gateway-service:2.0
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/gateway-service:3.0.0
container_name: gateway-service
env_file: docker-compose.env # 从文件中获取配置
restart: always
......@@ -17,7 +17,7 @@ services:
# 授权服务
# ---------------------------
auth-service:
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/auth-service:2.0
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/auth-service:3.0.0
container_name: auth-service
env_file: docker-compose.env # 从文件中获取配置
restart: always
......@@ -30,7 +30,7 @@ services:
# 用户服务
# ---------------------------
user-service:
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/user-service:2.0
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/user-service:3.0.0
container_name: user-service
env_file: docker-compose.env # 从文件中获取配置
restart: always
......@@ -43,7 +43,7 @@ services:
# 考试服务
# ---------------------------
exam-service:
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/exam-service:2.0
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/exam-service:3.0.0
container_name: exam-service
env_file: docker-compose.env # 从文件中获取配置
restart: always
......@@ -56,7 +56,7 @@ services:
# 监控服务
# ---------------------------
monitor-service:
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/monitor-service:2.0
image: registry.cn-hangzhou.aliyuncs.com/spring-microservice-exam/monitor-service:3.0.0
container_name: monitor-service
env_file: docker-compose.env # 从文件中获取配置
restart: always
......
// 租户标识,默认gitee
TENANT_CODE=gitee
// 环境配置
SPRING_PROFILES_ACTIVE=native
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.github.tangyi</groupId>
<artifactId>spring-microservice-exam</artifactId>
<version>2.0</version>
<version>3.0.0</version>
</parent>
<artifactId>gateway-service</artifactId>
<name>${project.artifactId}</name>
......
package com.github.tangyi.gateway.config;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.model.Route;
import com.github.tangyi.common.core.utils.JsonMapper;
import com.github.tangyi.common.core.vo.RoutePredicateVo;
import lombok.AllArgsConstructor;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* Swagger聚合文档
* 目前问题:结合动态更新路由之后,GatewayProperties获取不到新的路由列表,导致swagger-ui显示不了
* 解决办法:从Redis里读取路由数据
*
* @author tangyi
* @date 2019/3/26 15:39
*/
@Slf4j
@Component
@Primary
@AllArgsConstructor
......@@ -31,10 +36,10 @@ public class RegistrySwaggerResourcesProvider implements SwaggerResourcesProvide
private final RouteLocator routeLocator;
private final GatewayProperties gatewayProperties;
private final SwaggerProviderConfig swaggerProviderConfig;
private final RedisTemplate redisTemplate;
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
......@@ -45,17 +50,31 @@ public class RegistrySwaggerResourcesProvider implements SwaggerResourcesProvide
if (swaggerProviderConfig.getProviders().contains(route.getId()))
routes.add(route.getId());
});
// 结合配置的route-路径(Path),和route过滤,只获取有效的route节点
List<RouteDefinition> routeDefinitions = gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId())).collect(Collectors.toList());
routeDefinitions.forEach(routeDefinition -> {
List<PredicateDefinition> predicates = routeDefinition.getPredicates().stream()
.filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName())).collect(Collectors.toList());
predicates.forEach(predicateDefinition -> {
resources.add(swaggerResource(routeDefinition.getId(),
predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
.replace("/**", API_URI)));
});
});
Object object = redisTemplate.opsForValue().get(CommonConstant.ROUTE_KEY);
if (object != null) {
List<Route> routeList = JsonMapper.getInstance().fromJson(object.toString(), JsonMapper.getInstance().createCollectionType(ArrayList.class, Route.class));
if (CollectionUtils.isNotEmpty(routeList)) {
for (Route route : routeList) {
if (routes.contains(route.getRouteId())) {
try {
List<RoutePredicateVo> routePredicateVoList = JsonMapper.getInstance().fromJson(route.getPredicates(), JsonMapper.getInstance().createCollectionType(ArrayList.class, RoutePredicateVo.class));
if (CollectionUtils.isNotEmpty(routePredicateVoList)) {
String genKey;
RoutePredicateVo routePredicateVo = routePredicateVoList.stream().filter(routePredicate -> routePredicate.getArgs().containsKey(NameUtils.GENERATED_NAME_PREFIX + "0")).findFirst().orElse(null);
if (routePredicateVo != null) {
genKey = routePredicateVo.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0").replace("/**", API_URI);
resources.add(swaggerResource(route.getRouteId(), genKey));
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
}
}
return resources;
}
......
......@@ -11,6 +11,7 @@ import java.util.List;
/**
* 提供swagger文档的服务
*
* @author tangyi
* @date 2019/3/27 16:24
*/
......
......@@ -22,11 +22,6 @@ public class GatewayConstant {
public static final String REGISTER = "/user/register";
/**
* 密码类型
*/
public static final String GRANT_TYPE_PASSWORD = "password";
/**
* 刷新token类型
*/
public static final String GRANT_TYPE_REFRESH_TOKEN = "refresh_token";
......
......@@ -51,7 +51,7 @@ public class GatewayRouteController {
*
* @return Mono
*/
@GetMapping("/routeList")
@GetMapping("routeList")
public Mono<List<Map<String, Object>>> routes() {
Mono<Map<String, RouteDefinition>> routeDefs = this.routeDefinitionLocator
.getRouteDefinitions().collectMap(RouteDefinition::getId);
......@@ -135,7 +135,7 @@ public class GatewayRouteController {
* @author tangyi
* @date 2019/04/07 12:32
*/
@GetMapping("/refresh")
@GetMapping("refresh")
public ResponseBean<Boolean> refresh() {
amqpTemplate.convertAndSend(MqConstant.REFRESH_GATEWAY_ROUTE_QUEUE, "refresh");
return new ResponseBean<>(Boolean.TRUE);
......
......@@ -2,6 +2,7 @@ package com.github.tangyi.gateway.filters;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.gateway.constants.GatewayConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
......@@ -55,8 +56,8 @@ public class DecodePasswordFilter implements GlobalFilter, Ordered {
GatewayConstant.MOBILE_TOKEN_URL)) {
String grantType = request.getQueryParams().getFirst(GatewayConstant.GRANT_TYPE);
// 授权类型为密码模式则解密
if (GatewayConstant.GRANT_TYPE_PASSWORD.equals(grantType) || StrUtil.containsAnyIgnoreCase(uri.getPath(), GatewayConstant.REGISTER)) {
String password = request.getQueryParams().getFirst(GatewayConstant.GRANT_TYPE_PASSWORD);
if (CommonConstant.GRANT_TYPE_PASSWORD.equals(grantType) || StrUtil.containsAnyIgnoreCase(uri.getPath(), GatewayConstant.REGISTER)) {
String password = request.getQueryParams().getFirst(CommonConstant.GRANT_TYPE_PASSWORD);
if (password == null || password.isEmpty()) {
log.info("password is empty...");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
......@@ -70,7 +71,7 @@ public class DecodePasswordFilter implements GlobalFilter, Ordered {
} catch (Exception e) {
log.error("password decrypt fail:{}", password);
}
URI newUri = UriComponentsBuilder.fromUri(uri).replaceQueryParam(GatewayConstant.GRANT_TYPE_PASSWORD, password).build(true).toUri();
URI newUri = UriComponentsBuilder.fromUri(uri).replaceQueryParam(CommonConstant.GRANT_TYPE_PASSWORD, password).build(true).toUri();
request = request.mutate().uri(newUri).build();
return chain.filter(exchange.mutate().request(request).build());
}
......
package com.github.tangyi.gateway.filters;
import cn.hutool.core.util.StrUtil;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.exceptions.CommonException;
import com.github.tangyi.common.core.exceptions.InvalidAccessTokenException;
import com.github.tangyi.common.core.utils.JsonMapper;
import com.github.tangyi.gateway.constants.GatewayConstant;
import com.github.tangyi.gateway.model.AccessToken;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
......@@ -72,7 +75,7 @@ public class TokenRequestGlobalFilter implements GlobalFilter, Ordered {
&& StrUtil.containsAnyIgnoreCase(uri.getPath(), GatewayConstant.OAUTH_TOKEN_URL, GatewayConstant.REGISTER, GatewayConstant.MOBILE_TOKEN_URL)) {
String grantType = request.getQueryParams().getFirst(GatewayConstant.GRANT_TYPE);
// 授权类型为密码模式
if (GatewayConstant.GRANT_TYPE_PASSWORD.equals(grantType) || GatewayConstant.GRANT_TYPE_REFRESH_TOKEN.equals(grantType) || StrUtil.containsAnyIgnoreCase(uri.getPath(), GatewayConstant.REGISTER)) {
if (CommonConstant.GRANT_TYPE_PASSWORD.equals(grantType) || GatewayConstant.GRANT_TYPE_REFRESH_TOKEN.equals(grantType)) {
// 装饰器
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(exchange.getResponse()) {
@Override
......@@ -93,8 +96,8 @@ public class TokenRequestGlobalFilter implements GlobalFilter, Ordered {
String accessTokenContent = joiner.join(contentList);
log.trace("生成的accessToken: {}", accessTokenContent);
AccessToken accessToken = JsonMapper.getInstance().fromJson(accessTokenContent, AccessToken.class);
if (accessToken == null)
throw new CommonException("token转换失败!");
if (accessToken == null || StringUtils.isBlank(accessToken.getJti()))
throw new InvalidAccessTokenException("token转换失败!");
// 真正的access_token和refresh_token
String realAccessToken = accessToken.getAccessToken(), realRefreshToken = accessToken.getRefreshToken();
// 转换token
......
......@@ -2,6 +2,7 @@ package com.github.tangyi.gateway.filters;
import cn.hutool.core.util.StrUtil;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.exceptions.InvalidAccessTokenException;
import com.github.tangyi.gateway.constants.GatewayConstant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
......@@ -71,7 +72,10 @@ public class TokenResponseGlobalFilter implements GlobalFilter, Ordered {
log.trace("jti authorization: {}", authorization);
// 从Redis里获取实际的access_token
Object object = redisTemplate.opsForValue().get(GatewayConstant.GATEWAY_ACCESS_TOKENS + authorization);
authorization = object == null ? authorization : CommonConstant.TOKEN_SPLIT + object.toString();
// 缓存里没有access_token,抛异常,统一异常处理会返回403,前端自动重新获取access_token
if (object == null)
throw new InvalidAccessTokenException("access_token已失效.");
authorization = CommonConstant.TOKEN_SPLIT + object.toString();
String realAuthorization = authorization;
log.trace("jti->token:{}", realAuthorization);
// 更新请求头,参考源码:SetRequestHeaderGatewayFilterFactory
......
......@@ -41,7 +41,7 @@ public class ValidateCodeFilter implements GlobalFilter, Ordered {
&& StrUtil.containsAnyIgnoreCase(uri.getPath(), GatewayConstant.OAUTH_TOKEN_URL, GatewayConstant.REGISTER, GatewayConstant.MOBILE_TOKEN_URL)) {
String grantType = request.getQueryParams().getFirst(GatewayConstant.GRANT_TYPE);
// 授权类型为密码模式、注册才校验验证码
if (GatewayConstant.GRANT_TYPE_PASSWORD.equals(grantType) || StrUtil.containsAnyIgnoreCase(uri.getPath(), GatewayConstant.REGISTER)) {
if (CommonConstant.GRANT_TYPE_PASSWORD.equals(grantType) || StrUtil.containsAnyIgnoreCase(uri.getPath(), GatewayConstant.REGISTER)) {
// 校验验证码
checkCode(request);
}
......
package com.github.tangyi.gateway.handler;
import com.github.tangyi.common.core.exceptions.InvalidAccessTokenException;
import com.github.tangyi.common.core.exceptions.InvalidValidateCodeException;
import com.github.tangyi.common.core.exceptions.ValidateCodeExpiredException;
import com.github.tangyi.common.core.model.ResponseBean;
......@@ -84,7 +85,7 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
int code = ResponseBean.FAIL;
if (ex instanceof NotFoundException) {
httpStatus = HttpStatus.NOT_FOUND;
msg = "Service Not Found";
msg = "没有服务提供者.";
} else if (ex instanceof ResponseStatusException) {
ResponseStatusException responseStatusException = (ResponseStatusException) ex;
httpStatus = responseStatusException.getStatus();
......@@ -99,12 +100,17 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
msg = ex.getMessage();
// 验证码过期
code = ResponseBean.VALIDATE_CODE_EXPIRED_ERROR;
} else if (ex instanceof InvalidAccessTokenException) {
httpStatus = HttpStatus.FORBIDDEN;
msg = ex.getMessage();
// token非法,返回403
code = HttpStatus.FORBIDDEN.value();
} else {
httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
msg = "Internal Server Error";
msg = "服务器内部错误.";
}
// 封装响应体
ResponseBean<HttpStatus> responseBean = new ResponseBean<>(httpStatus, msg);
ResponseBean<String> responseBean = new ResponseBean<>(msg, msg);
responseBean.setStatus(httpStatus.value());
responseBean.setCode(code);
// 错误记录
......@@ -118,7 +124,6 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
.switchIfEmpty(Mono.error(ex))
.flatMap((handler) -> handler.handle(newRequest))
.flatMap((response) -> write(exchange, response));
}
private Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
......
......@@ -57,4 +57,9 @@ public class AccessToken implements Serializable {
*/
@JsonProperty("token_type")
private String tokenType;
/**
* 租户标识
*/
private String tenantCode;
}
......@@ -12,7 +12,6 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
......
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="false">
<springProperty scop="context" name="spring.application.name" source="spring.application.name" defaultValue="spring-microservice-exam"/>
<property name="log.path" value="logs/${spring.application.name}" />
<springProperty scop="context" name="spring.application.name" source="spring.application.name"
defaultValue="spring-microservice-exam"/>
<property name="log.path" value="logs/${spring.application.name}"/>
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
......@@ -40,8 +41,8 @@
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="debug" />
<appender-ref ref="error" />
<appender-ref ref="console"/>
<appender-ref ref="debug"/>
<appender-ref ref="error"/>
</root>
</configuration>
\ No newline at end of file
</configuration>
......@@ -31,7 +31,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
http.headers().frameOptions().disable();
http.authorizeRequests()
.antMatchers("/actuator/**", "/hystrix/**","/hystrix","*.sender").permitAll()
.antMatchers("/actuator/**", "/hystrix/**", "/hystrix", "*.sender").permitAll()
.antMatchers(adminContextPath + "/assets/**").permitAll()
.antMatchers(adminContextPath + "/login").permitAll()
.anyRequest().authenticated()
......
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="false">
<springProperty scop="context" name="spring.application.name" source="spring.application.name" defaultValue="spring-microservice-exam"/>
<property name="log.path" value="logs/${spring.application.name}" />
<springProperty scop="context" name="spring.application.name" source="spring.application.name"
defaultValue="spring-microservice-exam"/>
<property name="log.path" value="logs/${spring.application.name}"/>
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
......@@ -40,8 +41,8 @@
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="debug" />
<appender-ref ref="error" />
<appender-ref ref="console"/>
<appender-ref ref="debug"/>
<appender-ref ref="error"/>
</root>
</configuration>
\ No newline at end of file
</configuration>
......@@ -6,7 +6,7 @@
<groupId>com.github.tangyi</groupId>
<artifactId>spring-microservice-exam</artifactId>
<version>2.0</version>
<version>3.0.0</version>
<packaging>pom</packaging>
<name>spring-microservice-exam</name>
......
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.github.tangyi</groupId>
<artifactId>service-api-impl</artifactId>
<version>2.0</version>
<version>3.0.0</version>
</parent>
<artifactId>auth-service</artifactId>
<name>${project.artifactId}</name>
......
......@@ -2,7 +2,6 @@ package com.github.tangyi.auth;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
......
package com.github.tangyi.auth.config;
import com.github.tangyi.auth.security.CustomTokenConverter;
import com.github.tangyi.auth.security.TenantTokenFilter;
import com.github.tangyi.common.security.core.ClientDetailsServiceImpl;
import com.github.tangyi.common.security.exceptions.CustomOauthException;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -78,7 +80,7 @@ public class CustomAuthorizationServerConfigurer extends AuthorizationServerConf
@Bean
protected JwtAccessTokenConverter jwtTokenEnhancer() {
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(keyProperties.getKeyStore().getLocation(), keyProperties.getKeyStore().getPassword().toCharArray());
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
CustomTokenConverter converter = new CustomTokenConverter();
converter.setKeyPair(keyStoreKeyFactory.getKeyPair(keyProperties.getKeyStore().getAlias()));
return converter;
}
......@@ -143,7 +145,9 @@ public class CustomAuthorizationServerConfigurer extends AuthorizationServerConf
.tokenKeyAccess("permitAll()")
// 开启/oauth/check_token验证端口认证权限访问
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
.allowFormAuthenticationForClients()
// 增加租户信息过滤器
.addTokenEndpointAuthenticationFilter(new TenantTokenFilter());
}
}
package com.github.tangyi.auth.config;
import com.github.tangyi.auth.security.CustomUserDetailsAuthenticationProvider;
import com.github.tangyi.auth.security.CustomUserDetailsService;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import javax.annotation.Resource;
/**
* Spring Security配置
*
* @author tangyi
* @date 2019-03-14 14:35
*/
@AllArgsConstructor
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private UserDetailsService userDetailsService;
private final CustomUserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// 前后端分离,关闭csrf
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated();
}
@Bean
public BCryptPasswordEncoder encoder() {
......@@ -34,23 +44,24 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
}
@Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(encoder());
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authProvider());
}
@Override
/**
* 认证Provider,提供获取用户信息、认证、授权等功能
*
* @return AuthenticationProvider
*/
@Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
public AuthenticationProvider authProvider() {
return new CustomUserDetailsAuthenticationProvider(encoder(), userDetailsService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.
csrf().disable()
.authorizeRequests()
.anyRequest().authenticated();
@Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}
......@@ -57,13 +57,12 @@ public class SwaggerConfig implements WebMvcConfigurer {
.title("Swagger API")
.description("https://gitee.com/wells2333/spring-microservice-exam")
.termsOfServiceUrl("https://gitee.com/wells2333/spring-microservice-exam")
.contact(new Contact("tangyi","https://gitee.com/wells2333/spring-microservice-exam","1633736729@qq.com"))
.contact(new Contact("tangyi", "https://gitee.com/wells2333/spring-microservice-exam", "1633736729@qq.com"))
.version("2.0")
.build();
}
/**
*
* 显示swagger-ui.html文档展示页,还必须注入swagger资源:
*
* @param registry
......
......@@ -32,7 +32,7 @@ public class AuthenticationController extends BaseController {
* @param authentication 信息
* @return 用户信息
*/
@RequestMapping("/user")
@RequestMapping("user")
public Object user(Authentication authentication) {
return authentication.getPrincipal();
}
......@@ -43,7 +43,7 @@ public class AuthenticationController extends BaseController {
* @param accesstoken access_token
* @return ReturnT
*/
@PostMapping("/removeToken")
@PostMapping("removeToken")
public ResponseBean<Boolean> removeToken(@RequestHeader("Authorization") String accesstoken) {
if (accesstoken.startsWith(CommonConstant.AUTHORIZATION_BEARER))
accesstoken = accesstoken.split(CommonConstant.AUTHORIZATION_BEARER)[1];
......
......@@ -10,7 +10,6 @@ import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.utils.SecurityUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
......@@ -18,7 +17,6 @@ import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;
......@@ -120,7 +118,7 @@ public class OauthClientDetailsController extends BaseController {
@ApiImplicitParam(name = "oauthClientDetails", value = "客户端实体oauthClientDetails", required = true, dataType = "OauthClientDetails")
@Log("新增客户端")
public ResponseBean<Boolean> oauthClient(@RequestBody OauthClientDetails oauthClientDetails) {
oauthClientDetails.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
oauthClientDetails.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode());
// 加密密钥
oauthClientDetails.setClientSecret(bCryptPasswordEncoder.encode(oauthClientDetails.getClientSecretPlainText()));
return new ResponseBean<>(oauthClientDetailsService.insert(oauthClientDetails) > 0);
......@@ -144,7 +142,7 @@ public class OauthClientDetailsController extends BaseController {
// 有调整过明文则重新加密密钥
if (tempOauthClientDetails != null && !tempOauthClientDetails.getClientSecretPlainText().equals(oauthClientDetails.getClientSecretPlainText()))
oauthClientDetails.setClientSecret(bCryptPasswordEncoder.encode(oauthClientDetails.getClientSecretPlainText()));
oauthClientDetails.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
oauthClientDetails.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode());
return new ResponseBean<>(oauthClientDetailsService.update(oauthClientDetails) > 0);
}
......@@ -165,7 +163,7 @@ public class OauthClientDetailsController extends BaseController {
OauthClientDetails oauthClientDetails = new OauthClientDetails();
oauthClientDetails.setId(id);
oauthClientDetails.setNewRecord(false);
oauthClientDetails.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
oauthClientDetails.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode());
return new ResponseBean<>(oauthClientDetailsService.delete(oauthClientDetails) > 0);
}
......@@ -177,7 +175,7 @@ public class OauthClientDetailsController extends BaseController {
* @author tangyi
* @date 2019/03/30 17:01
*/
@PostMapping("/deleteAll")
@PostMapping("deleteAll")
@PreAuthorize("hasAuthority('sys:client:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "批量删除客户端", notes = "根据客户端id批量删除客户端")
@ApiImplicitParam(name = "oauthClientDetails", value = "客户端信息", dataType = "OauthClientDetails")
......
package com.github.tangyi.auth.model;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.util.Collection;
/**
* 用户信息
*
* @author tangyi
* @date 2019/5/28 21:13
*/
@Data
public class CustomUserDetails extends User {
private static final long serialVersionUID = 1L;
private String tenantCode;
/**
* 构造方法
*
* @param username username
* @param password password
* @param enabled enabled
* @param authorities authorities
* @param tenantCode tenantCode
*/
public CustomUserDetails(String username, String password, boolean enabled, Collection<? extends GrantedAuthority> authorities, String tenantCode) {
super(username, password, enabled, true, true, true, authorities);
this.tenantCode = tenantCode;
}
}
package com.github.tangyi.auth.security;
import com.github.tangyi.auth.model.CustomUserDetails;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.constant.ServiceConstant;
import com.github.tangyi.common.core.model.Log;
import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.user.api.feign.UserServiceClient;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.event.AbstractAuthenticationEvent;
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Component;
import java.security.Principal;
/**
* 监听登录成功事件,记录登录信息
*
* @author tangyi
* @date 2019-05-30 23:23
*/
@Slf4j
@AllArgsConstructor
@Component
public class CustomAuthenticationSuccessHandler {
private final UserServiceClient userServiceClient;
@EventListener({AuthenticationSuccessEvent.class, InteractiveAuthenticationSuccessEvent.class})
public void processAuthenticationSuccessEvent(AbstractAuthenticationEvent event) {
// 注意:登录包括oauth2客户端、用户名密码登录都会触发AuthenticationSuccessEvent,这里只记录用户名密码登录的日志
if (event.getAuthentication().getPrincipal() instanceof CustomUserDetails) {
CustomUserDetails userDetails = (CustomUserDetails) event.getAuthentication().getPrincipal();
String tenantCode = userDetails.getTenantCode();
String username = userDetails.getUsername();
log.info("CustomAuthenticationSuccessHandler->登录成功, tenantCode: {}, username: {}", tenantCode, username);
// 记录日志
Log logInfo = new Log();
logInfo.setCommonValue(username, SysUtil.getSysCode(), tenantCode);
logInfo.setTitle("用户登录");
logInfo.setType(CommonConstant.STATUS_NORMAL);
// 获取ip、浏览器信息
this.initLogInfo(logInfo, event.getSource());
logInfo.setServiceId(ServiceConstant.AUTH_SERVICE);
saveLoginSuccessLog(logInfo);
}
}
/**
* 参考源码:
* org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter
* org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails
*
* @param logInfo logInfo
* @param source source
*/
private void initLogInfo(Log logInfo, Object source) {
if (source instanceof OAuth2Authentication) {
OAuth2Authentication auth2Authentication = (OAuth2Authentication) source;
Object details = auth2Authentication.getDetails();
if (details instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails oAuth2AuthenticationDetails = (OAuth2AuthenticationDetails) details;
logInfo.setIp(oAuth2AuthenticationDetails.getRemoteAddress());
}
} else if (source instanceof UsernamePasswordAuthenticationToken) {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) source;
Object details = token.getDetails();
if (details instanceof WebAuthenticationDetails) {
WebAuthenticationDetails webAuthenticationDetails = (WebAuthenticationDetails) details;
logInfo.setIp(webAuthenticationDetails.getRemoteAddress());
}
}
}
/**
* 获取用户名
*
* @param authentication authentication
* @return String
*/
private String getUsername(Authentication authentication) {
String username = "";
Object principal = authentication.getPrincipal();
if (principal instanceof UserDetails) {
username = ((UserDetails) principal).getUsername();
} else if (principal instanceof Principal) {
username = ((Principal) principal).getName();
}
return username;
}
/**
* 异步记录登录日志
*
* @author tangyi
* @date 2019/05/30 23:30
*/
@Async
public void saveLoginSuccessLog(Log logInfo) {
try {
userServiceClient.saveLog(logInfo);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
package com.github.tangyi.auth.security;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.tenant.TenantContextHolder;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import java.util.HashMap;
import java.util.Map;
/**
* 扩展JwtAccessTokenConverter,增加租户code
*
* @author tangyi
* @date 2019/5/28 22:53
*/
public class CustomTokenConverter extends JwtAccessTokenConverter {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
if (authentication.getOAuth2Request().getGrantType().equalsIgnoreCase(CommonConstant.GRANT_TYPE_PASSWORD)) {
final Map<String, Object> additionalInfo = new HashMap<>();
// 加入tenantCode
additionalInfo.put("tenantCode", TenantContextHolder.getTenantCode());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
}
return super.enhance(accessToken, authentication);
}
}
package com.github.tangyi.auth.security;
import com.github.tangyi.common.core.exceptions.TenantNotFoundException;
import com.github.tangyi.common.core.tenant.TenantContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* 认证Provider,提供获取用户信息、认证、授权等功能
*
* @author tangyi
* @date 2019/5/28 21:26
*/
@Slf4j
public class CustomUserDetailsAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
private PasswordEncoder passwordEncoder;
private CustomUserDetailsService userDetailsService;
private String userNotFoundEncodedPassword;
public CustomUserDetailsAuthenticationProvider(PasswordEncoder passwordEncoder, CustomUserDetailsService userDetailsService) {
this.passwordEncoder = passwordEncoder;
this.userDetailsService = userDetailsService;
}
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
if (authentication.getCredentials() == null) {
log.debug("认证失败: 密码为空.");
throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "密码为空."));
}
// 获取密码
String presentedPassword = authentication.getCredentials().toString();
// 匹配密码
if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
log.debug("认证失败: 密码错误.");
throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "密码错误."));
}
}
@Override
protected void doAfterPropertiesSet() throws Exception {
if (this.userDetailsService == null)
throw new IllegalArgumentException("UserDetailsService不能为空.");
this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD);
}
/**
* 加载用户信息
*
* @param username username
* @param authentication authentication
* @return UserDetails
* @throws AuthenticationException
*/
@Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException, TenantNotFoundException{
UserDetails loadedUser;
try {
// 加载用户信息
loadedUser = this.userDetailsService.loadUserByUsernameAndTenantCode(authentication.getPrincipal().toString(), TenantContextHolder.getTenantCode());
} catch (TenantNotFoundException tenantNotFound) {
throw tenantNotFound;
} catch (UsernameNotFoundException notFound) {
if (authentication.getCredentials() != null) {
String presentedPassword = authentication.getCredentials().toString();
passwordEncoder.matches(presentedPassword, userNotFoundEncodedPassword);
}
throw notFound;
} catch (Exception repositoryProblem) {
throw new InternalAuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
}
if (loadedUser == null) {
throw new InternalAuthenticationServiceException("获取用户信息失败.");
}
return loadedUser;
}
}
package com.github.tangyi.auth.security;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
/**
* 查询用户信息接口
*
* @author tangyi
* @date 2019/5/28 21:05
*/
public interface CustomUserDetailsService {
/**
* 根据用户名和租户标识查询
*
* @param username username
* @param tenantCode tenantCode
* @return UserDetails
* @author tangyi
* @date 2019/05/28 21:06
*/
UserDetails loadUserByUsernameAndTenantCode(String username, String tenantCode) throws UsernameNotFoundException;
}
package com.github.tangyi.auth.service;
package com.github.tangyi.auth.security;
import com.github.tangyi.auth.model.CustomUserDetails;
import com.github.tangyi.auth.properties.SysProperties;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.exceptions.TenantNotFoundException;
import com.github.tangyi.common.core.vo.Role;
import com.github.tangyi.common.core.vo.UserVo;
import com.github.tangyi.common.security.core.GrantedAuthorityImpl;
import com.github.tangyi.common.security.core.UserDetailsImpl;
import com.github.tangyi.user.api.constant.MenuConstant;
import com.github.tangyi.user.api.feign.UserServiceClient;
import com.github.tangyi.user.api.module.Menu;
import com.github.tangyi.user.api.module.Tenant;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 模拟从数据库获取用户信息
* 从数据库获取用户信息
*
* @author tangyi
* @date 2019-03-14 14:36
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@AllArgsConstructor
@Service("userDetailsService")
public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
@Autowired
private UserServiceClient userServiceClient;
private final UserServiceClient userServiceClient;
@Autowired
private SysProperties sysProperties;
private final SysProperties sysProperties;
/**
* 加载用户信息
*
* @param username 用户名
* @return UserDetails
* @throws UsernameNotFoundException
* @throws UsernameNotFoundException,TenantNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails userDetails;
try {
UserVo userVo = userServiceClient.findUserByUsername(username);
if (userVo == null)
throw new UsernameNotFoundException("User name not found.");
userDetails = new UserDetailsImpl(username, userVo.getPassword(), userVo.getStatus(), getAuthority(userVo));
} catch (Exception e) {
throw new UsernameNotFoundException("Exception occurred wile reading user info.");
}
return userDetails;
public UserDetails loadUserByUsernameAndTenantCode(String username, String tenantCode) throws UsernameNotFoundException, TenantNotFoundException {
if (StringUtils.isBlank(tenantCode))
throw new TenantNotFoundException("租户code不能为空.");
// 先获取租户信息
Tenant tenant = userServiceClient.findTenantByTenantCode(tenantCode);
if (tenant == null)
throw new TenantNotFoundException("租户不存在.");
UserVo userVo = userServiceClient.findUserByUsername(username, tenantCode);
if (userVo == null)
throw new UsernameNotFoundException("用户名不存在.");
return new CustomUserDetails(username, userVo.getPassword(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode());
}
/**
......@@ -80,17 +82,16 @@ public class UserDetailsServiceImpl implements UserDetailsService {
// 判断是否是管理员,是则查找所有菜单权限
if (userVo.getUsername().equals(sysProperties.getAdminUser())) {
// 查找所有菜单权限,因为角色一般是一个,这里只会执行一次
menuStream = userServiceClient.findAllMenu().stream();
menuStream = userServiceClient.findAllMenu(userVo.getTenantCode()).stream();
} else {
// 根据角色查找菜单权限
menuStream = userServiceClient.findMenuByRole(role.getRoleCode()).stream();
}
if (Optional.ofNullable(menuStream).isPresent()) {
menuStream
// 菜单权限
.filter(menu -> MenuConstant.MENU_TYPE_PERMISSION.equals(menu.getType()))
.forEach(menu -> authorities.add(new GrantedAuthorityImpl(menu.getPermission())));
menuStream = userServiceClient.findMenuByRole(role.getRoleCode(), userVo.getTenantCode()).stream();
}
// 菜单权限
List<GrantedAuthority> menus = menuStream
.filter(menu -> MenuConstant.MENU_TYPE_PERMISSION.equals(menu.getType()))
.map(menu -> new GrantedAuthorityImpl(menu.getPermission())).collect(Collectors.toList());
authorities.addAll(menus);
});
}
return authorities;
......
package com.github.tangyi.auth.security;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.tenant.TenantContextHolder;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.*;
import java.io.IOException;
/**
* 获取请求里的租户code
*
* @author tangyi
* @date 2019/5/28 22:53
*/
@Slf4j
public class TenantTokenFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String tenantCode = servletRequest.getParameter(CommonConstant.TENANT_CODE);
if (tenantCode != null)
TenantContextHolder.setTenantCode(tenantCode);
log.info("租户code:{}", tenantCode);
filterChain.doFilter(servletRequest, servletResponse);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="false">
<springProperty scop="context" name="spring.application.name" source="spring.application.name" defaultValue="spring-microservice-exam"/>
<property name="log.path" value="logs/${spring.application.name}" />
<springProperty scop="context" name="spring.application.name" source="spring.application.name"
defaultValue="spring-microservice-exam"/>
<property name="log.path" value="logs/${spring.application.name}"/>
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
......@@ -40,8 +41,8 @@
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="debug" />
<appender-ref ref="error" />
<appender-ref ref="console"/>
<appender-ref ref="debug"/>
<appender-ref ref="error"/>
</root>
</configuration>
\ No newline at end of file
</configuration>
......@@ -185,7 +185,7 @@
del_flag = 1
WHERE id in
<foreach item="item" index="index" collection="array" open="("
separator="," close=")"> #{item}
separator="," close=")">#{item}
</foreach>
</delete>
</mapper>
......@@ -5,7 +5,7 @@
<parent>
<groupId>com.github.tangyi</groupId>
<artifactId>service-api-impl</artifactId>
<version>2.0</version>
<version>3.0.0</version>
</parent>
<artifactId>exam-service</artifactId>
<name>${project.artifactId}</name>
......
......@@ -20,8 +20,8 @@ import org.springframework.security.config.annotation.method.configuration.Enabl
@RefreshScope
public class ExamServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ExamServiceApplication.class, args);
}
public static void main(String[] args) {
SpringApplication.run(ExamServiceApplication.class, args);
}
}
......@@ -57,13 +57,12 @@ public class SwaggerConfig implements WebMvcConfigurer {
.title("Swagger API")
.description("https://gitee.com/wells2333/spring-microservice-exam")
.termsOfServiceUrl("https://gitee.com/wells2333/spring-microservice-exam")
.contact(new Contact("tangyi","https://gitee.com/wells2333/spring-microservice-exam","1633736729@qq.com"))
.contact(new Contact("tangyi", "https://gitee.com/wells2333/spring-microservice-exam", "1633736729@qq.com"))
.version("2.0")
.build();
}
/**
*
* 显示swagger-ui.html文档展示页,还必须注入swagger资源:
*
* @param registry
......
......@@ -7,7 +7,6 @@ import com.github.tangyi.common.core.utils.PageUtil;
import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.utils.SecurityUtil;
import com.github.tangyi.exam.api.dto.SubjectDto;
import com.github.tangyi.exam.api.module.Answer;
import com.github.tangyi.exam.service.AnswerService;
......@@ -17,9 +16,10 @@ import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
/**
* 答题controller
*
......@@ -48,11 +48,8 @@ public class AnswerController extends BaseController {
@ApiImplicitParam(name = "id", value = "答题ID", required = true, dataType = "String", paramType = "path")
public ResponseBean<Answer> answer(@PathVariable String id) {
Answer answer = new Answer();
if (StringUtils.isNotBlank(id)) {
answer.setId(id);
answer = answerService.get(answer);
}
return new ResponseBean<>(answer);
answer.setId(id);
return new ResponseBean<>(answerService.get(answer));
}
/**
......@@ -81,6 +78,7 @@ public class AnswerController extends BaseController {
@RequestParam(value = CommonConstant.SORT, required = false, defaultValue = CommonConstant.PAGE_SORT_DEFAULT) String sort,
@RequestParam(value = CommonConstant.ORDER, required = false, defaultValue = CommonConstant.PAGE_ORDER_DEFAULT) String order,
Answer answer) {
answer.setTenantCode(SysUtil.getTenantCode());
return answerService.findPage(PageUtil.pageInfo(pageNum, pageSize, sort, order), answer);
}
......@@ -96,8 +94,8 @@ public class AnswerController extends BaseController {
@ApiOperation(value = "创建答题", notes = "创建答题")
@ApiImplicitParam(name = "answer", value = "答题实体answer", required = true, dataType = "Answer")
@Log("新增答题")
public ResponseBean<Boolean> addAnswer(@RequestBody Answer answer) {
answer.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
public ResponseBean<Boolean> addAnswer(@RequestBody @Valid Answer answer) {
answer.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
return new ResponseBean<>(answerService.insert(answer) > 0);
}
......@@ -113,8 +111,8 @@ public class AnswerController extends BaseController {
@ApiOperation(value = "更新答题信息", notes = "根据答题id更新答题的基本信息")
@ApiImplicitParam(name = "answer", value = "答题实体answer", required = true, dataType = "Answer")
@Log("修改答题")
public ResponseBean<Boolean> updateAnswer(@RequestBody Answer answer) {
answer.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
public ResponseBean<Boolean> updateAnswer(@RequestBody @Valid Answer answer) {
answer.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
return new ResponseBean<>(answerService.update(answer) > 0);
}
......@@ -135,7 +133,7 @@ public class AnswerController extends BaseController {
try {
Answer answer = answerService.get(id);
if (answer != null) {
answer.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
answer.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
success = answerService.delete(answer) > 0;
}
} catch (Exception e) {
......@@ -156,7 +154,7 @@ public class AnswerController extends BaseController {
@ApiOperation(value = "保存答题", notes = "保存答题")
@ApiImplicitParam(name = "answer", value = "答题信息", dataType = "Answer")
@Log("保存答题")
public ResponseBean<Boolean> save(@RequestBody Answer answer) {
public ResponseBean<Boolean> save(@RequestBody @Valid Answer answer) {
return new ResponseBean<>(answerService.save(answer) > 0);
}
......@@ -171,7 +169,7 @@ public class AnswerController extends BaseController {
@PostMapping("saveAndNext")
@ApiOperation(value = "保存答题", notes = "保存答题")
@ApiImplicitParam(name = "answer", value = "答题信息", dataType = "Answer")
public ResponseBean<SubjectDto> saveAndNext(@RequestBody Answer answer) {
public ResponseBean<SubjectDto> saveAndNext(@RequestBody @Valid Answer answer) {
return new ResponseBean<>(answerService.saveAndNext(answer));
}
......
......@@ -8,7 +8,6 @@ import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.utils.SecurityUtil;
import com.github.tangyi.exam.api.module.Course;
import com.github.tangyi.exam.service.CourseService;
import io.swagger.annotations.Api;
......@@ -21,6 +20,8 @@ import org.apache.commons.lang.StringUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
/**
* 课程controller
*
......@@ -49,11 +50,8 @@ public class CourseController extends BaseController {
@ApiImplicitParam(name = "id", value = "课程ID", required = true, dataType = "String", paramType = "path")
public ResponseBean<Course> course(@PathVariable String id) {
Course course = new Course();
if (StringUtils.isNotBlank(id)) {
course.setId(id);
course = courseService.get(course);
}
return new ResponseBean<>(course);
course.setId(id);
return new ResponseBean<>(courseService.get(course));
}
/**
......@@ -82,6 +80,7 @@ public class CourseController extends BaseController {
@RequestParam(value = CommonConstant.SORT, required = false, defaultValue = CommonConstant.PAGE_SORT_DEFAULT) String sort,
@RequestParam(value = CommonConstant.ORDER, required = false, defaultValue = CommonConstant.PAGE_ORDER_DEFAULT) String order,
Course course) {
course.setTeacher(SysUtil.getTenantCode());
return courseService.findPage(PageUtil.pageInfo(pageNum, pageSize, sort, order), course);
}
......@@ -94,12 +93,12 @@ public class CourseController extends BaseController {
* @date 2018/11/10 21:31
*/
@PostMapping
@PreAuthorize("hasAuthority('exam:course:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PreAuthorize("hasAuthority('exam:course:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "创建课程", notes = "创建课程")
@ApiImplicitParam(name = "course", value = "课程实体course", required = true, dataType = "Course")
@Log("新增课程")
public ResponseBean<Boolean> addCourse(@RequestBody Course course) {
course.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
public ResponseBean<Boolean> addCourse(@RequestBody @Valid Course course) {
course.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
return new ResponseBean<>(courseService.insert(course) > 0);
}
......@@ -112,12 +111,12 @@ public class CourseController extends BaseController {
* @date 2018/11/10 21:31
*/
@PutMapping
@PreAuthorize("hasAuthority('exam:course:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PreAuthorize("hasAuthority('exam:course:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "更新课程信息", notes = "根据课程id更新课程的基本信息")
@ApiImplicitParam(name = "course", value = "课程实体course", required = true, dataType = "Course")
@Log("更新课程")
public ResponseBean<Boolean> updateCourse(@RequestBody Course course) {
course.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
public ResponseBean<Boolean> updateCourse(@RequestBody @Valid Course course) {
course.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
return new ResponseBean<>(courseService.update(course) > 0);
}
......@@ -130,7 +129,7 @@ public class CourseController extends BaseController {
* @date 2018/11/10 21:32
*/
@DeleteMapping("{id}")
@PreAuthorize("hasAuthority('exam:course:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PreAuthorize("hasAuthority('exam:course:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "删除课程", notes = "根据ID删除课程")
@ApiImplicitParam(name = "id", value = "课程ID", required = true, paramType = "path")
@Log("删除课程")
......@@ -141,7 +140,7 @@ public class CourseController extends BaseController {
course.setId(id);
course = courseService.get(course);
if (course != null) {
course.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
course.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
success = courseService.delete(course) > 0;
}
} catch (Exception e) {
......@@ -158,8 +157,8 @@ public class CourseController extends BaseController {
* @author tangyi
* @date 2018/12/4 11:26
*/
@PostMapping("/deleteAll")
@PreAuthorize("hasAuthority('exam:course:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PostMapping("deleteAll")
@PreAuthorize("hasAuthority('exam:course:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "批量删除课程", notes = "根据课程id批量删除课程")
@ApiImplicitParam(name = "course", value = "课程信息", dataType = "Course")
@Log("批量删除课程")
......
......@@ -12,7 +12,7 @@ import java.security.Principal;
@RestController
public class ExamController {
@GetMapping("/sayHello")
@GetMapping("sayHello")
public String sayHello(Principal principal, String name) {
return "hello, " + name + ", principal: " + principal.toString();
}
......
......@@ -2,7 +2,6 @@ package com.github.tangyi.exam.controller;
import com.github.pagehelper.PageInfo;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.exceptions.CommonException;
import com.github.tangyi.common.core.model.ResponseBean;
import com.github.tangyi.common.core.utils.*;
import com.github.tangyi.common.core.vo.DeptVo;
......@@ -10,7 +9,6 @@ import com.github.tangyi.common.core.vo.UserVo;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.utils.SecurityUtil;
import com.github.tangyi.exam.api.dto.ExamRecordDto;
import com.github.tangyi.exam.api.dto.StartExamDto;
import com.github.tangyi.exam.api.module.ExamRecord;
......@@ -35,6 +33,7 @@ import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
......@@ -76,11 +75,8 @@ public class ExamRecordController extends BaseController {
@ApiImplicitParam(name = "id", value = "考试记录ID", required = true, dataType = "String", paramType = "path")
public ResponseBean<ExamRecord> examRecord(@PathVariable String id) {
ExamRecord examRecord = new ExamRecord();
if (StringUtils.isNotBlank(id)) {
examRecord.setId(id);
examRecord = examRecordService.get(examRecord);
}
return new ResponseBean<>(examRecord);
examRecord.setId(id);
return new ResponseBean<>(examRecordService.get(examRecord));
}
/**
......@@ -109,6 +105,7 @@ public class ExamRecordController extends BaseController {
@RequestParam(value = CommonConstant.SORT, required = false, defaultValue = CommonConstant.PAGE_SORT_DEFAULT) String sort,
@RequestParam(value = CommonConstant.ORDER, required = false, defaultValue = CommonConstant.PAGE_ORDER_DEFAULT) String order,
ExamRecord examRecord) {
examRecord.setTenantCode(SysUtil.getTenantCode());
PageInfo<ExamRecordDto> examRecordDtoPageInfo = new PageInfo<>();
List<ExamRecordDto> examRecordDtoList = new ArrayList<>();
// 查询考试记录
......@@ -194,11 +191,7 @@ public class ExamRecordController extends BaseController {
@ApiOperation(value = "创建考试记录", notes = "创建考试记录")
@ApiImplicitParam(name = "examRecord", value = "考试记录实体examRecord", required = true, dataType = "ExamRecord")
@Log("新增考试记录")
public ResponseBean<ExamRecord> addExamRecord(@RequestBody ExamRecord examRecord) {
if (StringUtils.isEmpty(examRecord.getExaminationId()))
throw new CommonException("参数校验失败,考试id为空!");
if (StringUtils.isEmpty(examRecord.getUserId()))
throw new CommonException("参数校验失败,用户id为空!");
public ResponseBean<ExamRecord> addExamRecord(@RequestBody @Valid ExamRecord examRecord) {
Examination examination = new Examination();
examination.setId(examRecord.getExaminationId());
// 查找考试信息
......@@ -207,7 +200,7 @@ public class ExamRecordController extends BaseController {
examRecord.setExaminationName(examination.getExaminationName());
examRecord.setCourseId(examination.getCourseId());
}
examRecord.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
examRecord.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
examRecord.setStartTime(examRecord.getCreateDate());
examRecordService.insert(examRecord);
return new ResponseBean<>(examRecord);
......@@ -225,8 +218,8 @@ public class ExamRecordController extends BaseController {
@ApiOperation(value = "更新考试记录信息", notes = "根据考试记录id更新考试记录的基本信息")
@ApiImplicitParam(name = "examRecord", value = "考试记录实体examRecord", required = true, dataType = "ExamRecord")
@Log("更新考试记录")
public ResponseBean<Boolean> updateExamRecord(@RequestBody ExamRecord examRecord) {
examRecord.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
public ResponseBean<Boolean> updateExamRecord(@RequestBody @Valid ExamRecord examRecord) {
examRecord.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
return new ResponseBean<>(examRecordService.update(examRecord) > 0);
}
......@@ -247,7 +240,7 @@ public class ExamRecordController extends BaseController {
try {
ExamRecord examRecord = examRecordService.get(id);
if (examRecord != null) {
examRecord.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
examRecord.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
success = examRecordService.delete(examRecord) > 0;
}
} catch (Exception e) {
......@@ -263,8 +256,8 @@ public class ExamRecordController extends BaseController {
* @author tangyi
* @date 2018/12/31 22:28
*/
@PostMapping("/export")
@PreAuthorize("hasAuthority('exam:examRecord:export') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PostMapping("export")
@PreAuthorize("hasAuthority('exam:examRecord:export') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "导出考试成绩", notes = "根据成绩id导出成绩")
@ApiImplicitParam(name = "examRecordDto", value = "成绩信息", required = true, dataType = "ExamRecordDto")
@Log("导出考试记录")
......@@ -282,7 +275,9 @@ public class ExamRecordController extends BaseController {
examRecordList = examRecordService.findListById(examRecord);
} else {
// 导出全部
examRecordList = examRecordService.findList(new ExamRecord());
ExamRecord examRecord = new ExamRecord();
examRecord.setTenantCode(SysUtil.getTenantCode());
examRecordList = examRecordService.findList(examRecord);
}
// 查询考试、用户、部门数据
if (CollectionUtils.isNotEmpty(examRecordList)) {
......
......@@ -8,7 +8,6 @@ import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.utils.SecurityUtil;
import com.github.tangyi.exam.api.dto.ExaminationDto;
import com.github.tangyi.exam.api.module.Course;
import com.github.tangyi.exam.api.module.Examination;
......@@ -26,6 +25,8 @@ import org.springframework.beans.BeanUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.util.List;
import java.util.stream.Collectors;
......@@ -59,11 +60,8 @@ public class ExaminationController extends BaseController {
@ApiImplicitParam(name = "id", value = "考试ID", required = true, dataType = "String", paramType = "path")
public ResponseBean<Examination> examination(@PathVariable String id) {
Examination examination = new Examination();
if (StringUtils.isNotBlank(id)) {
examination.setId(id);
examination = examinationService.get(examination);
}
return new ResponseBean<>(examination);
examination.setId(id);
return new ResponseBean<>(examinationService.get(examination));
}
/**
......@@ -92,6 +90,7 @@ public class ExaminationController extends BaseController {
@RequestParam(value = CommonConstant.SORT, required = false, defaultValue = CommonConstant.PAGE_SORT_DEFAULT) String sort,
@RequestParam(value = CommonConstant.ORDER, required = false, defaultValue = CommonConstant.PAGE_ORDER_DEFAULT) String order,
Examination examination) {
examination.setTenantCode(SysUtil.getTenantCode());
PageInfo<Examination> page = examinationService.findPage(PageUtil.pageInfo(pageNum, pageSize, sort, order), examination);
PageInfo<ExaminationDto> examinationDtoPageInfo = new PageInfo<>();
BeanUtils.copyProperties(page, examinationDtoPageInfo);
......@@ -122,15 +121,15 @@ public class ExaminationController extends BaseController {
* @date 2018/11/10 21:14
*/
@PostMapping
@PreAuthorize("hasAuthority('exam:exam:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PreAuthorize("hasAuthority('exam:exam:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "创建考试", notes = "创建考试")
@ApiImplicitParam(name = "examinationDto", value = "考试实体examinationDto", required = true, dataType = "ExaminationDto")
@Log("新增考试")
public ResponseBean<Boolean> addExamination(@RequestBody ExaminationDto examinationDto) {
public ResponseBean<Boolean> addExamination(@RequestBody @Valid ExaminationDto examinationDto) {
Examination examination = new Examination();
BeanUtils.copyProperties(examinationDto, examination);
examination.setCourseId(examinationDto.getCourse().getId());
examination.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
examination.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
return new ResponseBean<>(examinationService.insert(examination) > 0);
}
......@@ -143,15 +142,15 @@ public class ExaminationController extends BaseController {
* @date 2018/11/10 21:15
*/
@PutMapping
@PreAuthorize("hasAuthority('exam:exam:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PreAuthorize("hasAuthority('exam:exam:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "更新考试信息", notes = "根据考试id更新考试的基本信息")
@ApiImplicitParam(name = "examinationDto", value = "考试实体answer", required = true, dataType = "ExaminationDto")
@Log("更新考试")
public ResponseBean<Boolean> updateExamination(@RequestBody ExaminationDto examinationDto) {
public ResponseBean<Boolean> updateExamination(@RequestBody @Valid ExaminationDto examinationDto) {
Examination examination = new Examination();
BeanUtils.copyProperties(examinationDto, examination);
examination.setCourseId(examinationDto.getCourse().getId());
examination.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
examination.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
return new ResponseBean<>(examinationService.update(examination) > 0);
}
......@@ -164,7 +163,7 @@ public class ExaminationController extends BaseController {
* @date 2018/11/10 21:20
*/
@DeleteMapping("{id}")
@PreAuthorize("hasAuthority('exam:exam:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PreAuthorize("hasAuthority('exam:exam:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "删除考试", notes = "根据ID删除考试")
@ApiImplicitParam(name = "id", value = "考试ID", required = true, paramType = "path")
@Log("删除考试")
......@@ -175,7 +174,7 @@ public class ExaminationController extends BaseController {
examination.setId(id);
examination = examinationService.get(examination);
if (examination != null) {
examination.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
examination.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
success = examinationService.delete(examination) > 0;
}
} catch (Exception e) {
......@@ -192,8 +191,8 @@ public class ExaminationController extends BaseController {
* @author tangyi
* @date 2018/12/03 22:03
*/
@PostMapping("/deleteAll")
@PreAuthorize("hasAuthority('exam:exam:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PostMapping("deleteAll")
@PreAuthorize("hasAuthority('exam:exam:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "批量删除考试", notes = "根据考试id批量删除考试")
@ApiImplicitParam(name = "examinationDto", value = "考试信息", dataType = "ExaminationDto")
@Log("批量删除考试")
......@@ -211,14 +210,15 @@ public class ExaminationController extends BaseController {
/**
* 查询考试数量
*
* @param tenantCode 租户标识
* @return ResponseBean
* @author tangyi
* @date 2019/3/1 15:30
*/
@GetMapping("/examinationCount")
public ResponseBean<Integer> findExaminationCount() {
@GetMapping("examinationCount")
public ResponseBean<Integer> findExaminationCount(@RequestParam @NotBlank String tenantCode) {
Examination examination = new Examination();
examination.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
examination.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), tenantCode);
return new ResponseBean<>(examinationService.findExaminationCount(examination));
}
}
......@@ -7,7 +7,6 @@ import com.github.tangyi.common.core.utils.PageUtil;
import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.utils.SecurityUtil;
import com.github.tangyi.exam.api.dto.IncorrectAnswerDto;
import com.github.tangyi.exam.api.module.IncorrectAnswer;
import com.github.tangyi.exam.api.module.Subject;
......@@ -20,10 +19,10 @@ import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
......@@ -57,11 +56,8 @@ public class IncorrectAnswerController extends BaseController {
@ApiImplicitParam(name = "id", value = "错题ID", required = true, dataType = "String", paramType = "path")
public ResponseBean<IncorrectAnswer> examRecord(@PathVariable String id) {
IncorrectAnswer incorrectAnswer = new IncorrectAnswer();
if (StringUtils.isNotBlank(id)) {
incorrectAnswer.setId(id);
incorrectAnswer = incorrectAnswerService.get(incorrectAnswer);
}
return new ResponseBean<>(incorrectAnswer);
incorrectAnswer.setId(id);
return new ResponseBean<>(incorrectAnswerService.get(incorrectAnswer));
}
/**
......@@ -90,6 +86,7 @@ public class IncorrectAnswerController extends BaseController {
@RequestParam(value = CommonConstant.SORT, required = false, defaultValue = CommonConstant.PAGE_SORT_DEFAULT) String sort,
@RequestParam(value = CommonConstant.ORDER, required = false, defaultValue = CommonConstant.PAGE_ORDER_DEFAULT) String order,
IncorrectAnswer incorrectAnswer) {
incorrectAnswer.setTenantCode(SysUtil.getTenantCode());
// 查找错题
PageInfo<IncorrectAnswer> incorrectAnswerPageInfo = incorrectAnswerService.findPage(PageUtil.pageInfo(pageNum, pageSize, sort, order), incorrectAnswer);
PageInfo<IncorrectAnswerDto> pageInfo = new PageInfo<>();
......@@ -132,8 +129,8 @@ public class IncorrectAnswerController extends BaseController {
@ApiOperation(value = "创建错题", notes = "创建错题")
@ApiImplicitParam(name = "incorrectAnswer", value = "错题实体incorrectAnswer", required = true, dataType = "IncorrectAnswer")
@Log("新增错题")
public ResponseBean<Boolean> addIncorrectAnswer(@RequestBody IncorrectAnswer incorrectAnswer) {
incorrectAnswer.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
public ResponseBean<Boolean> addIncorrectAnswer(@RequestBody @Valid IncorrectAnswer incorrectAnswer) {
incorrectAnswer.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
return new ResponseBean<>(incorrectAnswerService.insert(incorrectAnswer) > 0);
}
......@@ -149,8 +146,8 @@ public class IncorrectAnswerController extends BaseController {
@ApiOperation(value = "更新错题信息", notes = "根据错题id更新错题的基本信息")
@ApiImplicitParam(name = "incorrectAnswer", value = "错题实体incorrectAnswer", required = true, dataType = "IncorrectAnswer")
@Log("更新错题")
public ResponseBean<Boolean> updateIncorrectAnswer(@RequestBody IncorrectAnswer incorrectAnswer) {
incorrectAnswer.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
public ResponseBean<Boolean> updateIncorrectAnswer(@RequestBody @Valid IncorrectAnswer incorrectAnswer) {
incorrectAnswer.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
return new ResponseBean<>(incorrectAnswerService.update(incorrectAnswer) > 0);
}
......@@ -171,7 +168,7 @@ public class IncorrectAnswerController extends BaseController {
try {
IncorrectAnswer incorrectAnswer = incorrectAnswerService.get(id);
if (incorrectAnswer != null) {
incorrectAnswer.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
incorrectAnswer.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
success = incorrectAnswerService.delete(incorrectAnswer) > 0;
}
} catch (Exception e) {
......
......@@ -8,7 +8,6 @@ import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.core.vo.AttachmentVo;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.utils.SecurityUtil;
import com.github.tangyi.exam.api.dto.KnowledgeDto;
import com.github.tangyi.exam.api.module.Knowledge;
import com.github.tangyi.exam.service.KnowledgeService;
......@@ -24,6 +23,7 @@ import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
......@@ -59,11 +59,8 @@ public class KnowledgeController extends BaseController {
@ApiImplicitParam(name = "id", value = "知识ID", required = true, dataType = "String", paramType = "path")
public ResponseBean<Knowledge> knowledge(@PathVariable String id) {
Knowledge knowledge = new Knowledge();
if (StringUtils.isNotBlank(id)) {
knowledge.setId(id);
knowledge = knowledgeService.get(knowledge);
}
return new ResponseBean<>(knowledge);
knowledge.setId(id);
return new ResponseBean<>(knowledgeService.get(knowledge));
}
/**
......@@ -92,6 +89,7 @@ public class KnowledgeController extends BaseController {
@RequestParam(value = CommonConstant.SORT, required = false, defaultValue = CommonConstant.PAGE_SORT_DEFAULT) String sort,
@RequestParam(value = CommonConstant.ORDER, required = false, defaultValue = CommonConstant.PAGE_ORDER_DEFAULT) String order,
Knowledge knowledge) {
knowledge.setTenantCode(SysUtil.getTenantCode());
// 查询知识
PageInfo<Knowledge> knowledgePageInfo = knowledgeService.findPage(PageUtil.pageInfo(pageNum, pageSize, sort, order), knowledge);
PageInfo<KnowledgeDto> knowledgeDtoPageInfo = new PageInfo<>();
......@@ -147,8 +145,8 @@ public class KnowledgeController extends BaseController {
@ApiOperation(value = "创建知识", notes = "创建知识")
@ApiImplicitParam(name = "knowledge", value = "知识实体knowledge", required = true, dataType = "Knowledge")
@Log("新增知识")
public ResponseBean<Boolean> addKnowledge(@RequestBody Knowledge knowledge) {
knowledge.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
public ResponseBean<Boolean> addKnowledge(@RequestBody @Valid Knowledge knowledge) {
knowledge.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
return new ResponseBean<>(knowledgeService.insert(knowledge) > 0);
}
......@@ -164,8 +162,8 @@ public class KnowledgeController extends BaseController {
@ApiOperation(value = "更新知识信息", notes = "根据知识id更新知识的基本信息")
@ApiImplicitParam(name = "knowledge", value = "知识实体knowledge", required = true, dataType = "Knowledge")
@Log("更新知识")
public ResponseBean<Boolean> updateKnowledge(@RequestBody Knowledge knowledge) {
knowledge.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
public ResponseBean<Boolean> updateKnowledge(@RequestBody @Valid Knowledge knowledge) {
knowledge.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
return new ResponseBean<>(knowledgeService.update(knowledge) > 0);
}
......@@ -188,7 +186,7 @@ public class KnowledgeController extends BaseController {
knowledge.setId(id);
knowledge = knowledgeService.get(knowledge);
if (knowledge != null) {
knowledge.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
knowledge.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
success = knowledgeService.delete(knowledge) > 0;
}
// 删除附件
......@@ -208,7 +206,7 @@ public class KnowledgeController extends BaseController {
* @author tangyi
* @date 2019/1/1 15:15
*/
@PostMapping("/deleteAll")
@PostMapping("deleteAll")
@ApiOperation(value = "批量删除知识", notes = "根据知识id批量删除知识")
@ApiImplicitParam(name = "knowledge", value = "知识信息", dataType = "Knowledge")
@Log("批量删除知识")
......
......@@ -7,7 +7,6 @@ import com.github.tangyi.common.core.utils.*;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.utils.SecurityUtil;
import com.github.tangyi.exam.api.dto.SubjectBankDto;
import com.github.tangyi.exam.api.module.SubjectBank;
import com.github.tangyi.exam.api.module.SubjectCategory;
......@@ -20,13 +19,14 @@ import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
......@@ -61,11 +61,8 @@ public class SubjectBankController extends BaseController {
@ApiImplicitParam(name = "id", value = "题库ID", required = true, dataType = "String", paramType = "path")
public ResponseBean<SubjectBank> subjectBank(@PathVariable String id) {
SubjectBank subjectBank = new SubjectBank();
if (StringUtils.isNotBlank(id)) {
subjectBank.setId(id);
subjectBank = subjectBankService.get(subjectBank);
}
return new ResponseBean<>(subjectBank);
subjectBank.setId(id);
return new ResponseBean<>(subjectBankService.get(subjectBank));
}
/**
......@@ -94,6 +91,7 @@ public class SubjectBankController extends BaseController {
@RequestParam(value = CommonConstant.SORT, required = false, defaultValue = CommonConstant.PAGE_SORT_DEFAULT) String sort,
@RequestParam(value = CommonConstant.ORDER, required = false, defaultValue = CommonConstant.PAGE_ORDER_DEFAULT) String order,
SubjectBank subjectBank) {
subjectBank.setTenantCode(SysUtil.getTenantCode());
PageInfo<SubjectBank> page = subjectBankService.findPage(PageUtil.pageInfo(pageNum, pageSize, sort, order), subjectBank);
if (CollectionUtils.isNotEmpty(page.getList())) {
// 查询分类信息
......@@ -125,12 +123,12 @@ public class SubjectBankController extends BaseController {
* @date 2018/12/9 14:14
*/
@PostMapping
@PreAuthorize("hasAuthority('exam:subject:bank:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PreAuthorize("hasAuthority('exam:subject:bank:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "创建题库", notes = "创建题库")
@ApiImplicitParam(name = "subjectBank", value = "题库实体subjectBank", required = true, dataType = "SubjectBank")
@Log("新增题库")
public ResponseBean<Boolean> addSubjectBank(@RequestBody SubjectBank subjectBank) {
subjectBank.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
public ResponseBean<Boolean> addSubjectBank(@RequestBody @Valid SubjectBank subjectBank) {
subjectBank.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
return new ResponseBean<>(subjectBankService.insert(subjectBank) > 0);
}
......@@ -143,12 +141,12 @@ public class SubjectBankController extends BaseController {
* @date 2018/12/9 14:15
*/
@PutMapping
@PreAuthorize("hasAuthority('exam:subject:bank:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PreAuthorize("hasAuthority('exam:subject:bank:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "更新题库信息", notes = "根据题库id更新题库的基本信息")
@ApiImplicitParam(name = "subjectBank", value = "题库实体subjectBank", required = true, dataType = "SubjectBank")
@Log("更新题库")
public ResponseBean<Boolean> updateSubjectBank(@RequestBody SubjectBank subjectBank) {
subjectBank.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
public ResponseBean<Boolean> updateSubjectBank(@RequestBody @Valid SubjectBank subjectBank) {
subjectBank.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
return new ResponseBean<>(subjectBankService.update(subjectBank) > 0);
}
......@@ -161,7 +159,7 @@ public class SubjectBankController extends BaseController {
* @date 2018/12/9 14:15
*/
@DeleteMapping("{id}")
@PreAuthorize("hasAuthority('exam:subject:bank:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PreAuthorize("hasAuthority('exam:subject:bank:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "删除题库", notes = "根据ID删除题库")
@ApiImplicitParam(name = "id", value = "题库ID", required = true, paramType = "path")
@Log("删除题库")
......@@ -172,7 +170,7 @@ public class SubjectBankController extends BaseController {
subjectBank.setId(id);
subjectBank = subjectBankService.get(subjectBank);
if (subjectBank != null) {
subjectBank.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
subjectBank.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
success = subjectBankService.delete(subjectBank) > 0;
}
} catch (Exception e) {
......@@ -188,8 +186,8 @@ public class SubjectBankController extends BaseController {
* @author tangyi
* @date 2018/12/9 14:16
*/
@PostMapping("/export")
@PreAuthorize("hasAuthority('exam:subject:bank:export') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PostMapping("export")
@PreAuthorize("hasAuthority('exam:subject:bank:export') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "导出题目", notes = "根据分类id导出题目")
@ApiImplicitParam(name = "subjectBankDto", value = "分类信息", required = true, dataType = "SubjectBankDto")
@Log("导出题库题目")
......@@ -229,12 +227,12 @@ public class SubjectBankController extends BaseController {
* @author tangyi
* @date 2018/12/9 14:19
*/
@RequestMapping("/import")
@PreAuthorize("hasAuthority('exam:subject:bank:import') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@RequestMapping("import")
@PreAuthorize("hasAuthority('exam:subject:bank:import') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "导入题目", notes = "导入题目")
@ApiImplicitParam(name = "categoryId", value = "分类ID", required = true, dataType = "String")
@Log("导入题库题目")
public ResponseBean<Boolean> importSubjectBank(String categoryId, @ApiParam(value = "要上传的文件", required = true) MultipartFile file) {
public ResponseBean<Boolean> importSubjectBank(@NotBlank String categoryId, @ApiParam(value = "要上传的文件", required = true) MultipartFile file) {
try {
log.debug("开始导入题目数据,分类ID:{}", categoryId);
List<SubjectBank> subjectBanks = MapUtil.map2Java(SubjectBank.class,
......@@ -243,13 +241,12 @@ public class SubjectBankController extends BaseController {
for (SubjectBank subjectBank : subjectBanks) {
// 初始化考试ID
if (StringUtils.isBlank(subjectBank.getId())) {
subjectBank.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
subjectBank.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
subjectBank.setCategoryId(categoryId);
subjectBankService.insert(subjectBank);
} else {
// 绑定分类ID
if (StringUtils.isNotBlank(categoryId))
subjectBank.setCategoryId(categoryId);
subjectBank.setCategoryId(categoryId);
subjectBankService.update(subjectBank);
}
}
......@@ -269,8 +266,8 @@ public class SubjectBankController extends BaseController {
* @author tangyi
* @date 2018/12/04 9:55
*/
@PostMapping("/deleteAll")
@PreAuthorize("hasAuthority('exam:subject:bank:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PostMapping("deleteAll")
@PreAuthorize("hasAuthority('exam:subject:bank:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "批量删除题目", notes = "根据题目id批量删除题目")
@ApiImplicitParam(name = "subjectBankDto", value = "题目信息", dataType = "SubjectBankDto")
@Log("批量删除题库题目")
......
......@@ -7,7 +7,6 @@ import com.github.tangyi.common.core.utils.TreeUtil;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.utils.SecurityUtil;
import com.github.tangyi.exam.api.dto.SubjectCategoryDto;
import com.github.tangyi.exam.api.module.SubjectCategory;
import com.github.tangyi.exam.service.SubjectCategoryService;
......@@ -16,10 +15,10 @@ import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
......@@ -46,11 +45,13 @@ public class SubjectCategoryController extends BaseController {
* @author tangyi
* @date 2018/12/04 22:03
*/
@GetMapping(value = "/categories")
@GetMapping(value = "categories")
@ApiOperation(value = "获取分类列表")
public List<SubjectCategoryDto> menus() {
SubjectCategory subjectCategory = new SubjectCategory();
subjectCategory.setTenantCode(SysUtil.getTenantCode());
// 查询所有分类
List<SubjectCategory> subjectCategoryList = categoryService.findList(new SubjectCategory());
List<SubjectCategory> subjectCategoryList = categoryService.findList(subjectCategory);
if (CollectionUtils.isNotEmpty(subjectCategoryList)) {
// 转成dto
List<SubjectCategoryDto> subjectCategorySetTreeList = subjectCategoryList.stream().map(SubjectCategoryDto::new).distinct().collect(Collectors.toList());
......@@ -73,11 +74,8 @@ public class SubjectCategoryController extends BaseController {
@ApiImplicitParam(name = "id", value = "分类ID", required = true, dataType = "String", paramType = "path")
public ResponseBean<SubjectCategory> subjectCategory(@PathVariable String id) {
SubjectCategory subjectCategory = new SubjectCategory();
if (StringUtils.isNotBlank(id)) {
subjectCategory.setId(id);
subjectCategory = categoryService.get(subjectCategory);
}
return new ResponseBean<>(subjectCategory);
subjectCategory.setId(id);
return new ResponseBean<>(categoryService.get(subjectCategory));
}
/**
......@@ -89,12 +87,12 @@ public class SubjectCategoryController extends BaseController {
* @date 2018/12/04 22:00
*/
@PostMapping
@PreAuthorize("hasAuthority('exam:subject:category:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PreAuthorize("hasAuthority('exam:subject:category:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "创建分类", notes = "创建分类")
@ApiImplicitParam(name = "subjectCategory", value = "分类实体subjectCategory", required = true, dataType = "SubjectCategory")
@Log("新增题目分类")
public ResponseBean<Boolean> addSubjectCategory(@RequestBody SubjectCategory subjectCategory) {
subjectCategory.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
public ResponseBean<Boolean> addSubjectCategory(@RequestBody @Valid SubjectCategory subjectCategory) {
subjectCategory.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
return new ResponseBean<>(categoryService.insert(subjectCategory) > 0);
}
......@@ -107,12 +105,12 @@ public class SubjectCategoryController extends BaseController {
* @date 2018/12/04 22:01
*/
@PutMapping
@PreAuthorize("hasAuthority('exam:subject:category:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PreAuthorize("hasAuthority('exam:subject:category:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "更新分类信息", notes = "根据分类id更新分类的基本信息")
@ApiImplicitParam(name = "subjectCategory", value = "分类实体subjectCategory", required = true, dataType = "SubjectCategory")
@Log("更新题目分类")
public ResponseBean<Boolean> updateSubjectCategory(@RequestBody SubjectCategory subjectCategory) {
subjectCategory.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
public ResponseBean<Boolean> updateSubjectCategory(@RequestBody @Valid SubjectCategory subjectCategory) {
subjectCategory.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
return new ResponseBean<>(categoryService.update(subjectCategory) > 0);
}
......@@ -125,7 +123,7 @@ public class SubjectCategoryController extends BaseController {
* @date 2018/12/04 22:02
*/
@DeleteMapping("/{id}")
@PreAuthorize("hasAuthority('exam:subject:category:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PreAuthorize("hasAuthority('exam:subject:category:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "删除分类", notes = "根据ID删除分类")
@ApiImplicitParam(name = "id", value = "分类ID", required = true, paramType = "path")
@Log("删除题目分类")
......
......@@ -7,7 +7,6 @@ import com.github.tangyi.common.core.utils.*;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.utils.SecurityUtil;
import com.github.tangyi.exam.api.dto.SubjectDto;
import com.github.tangyi.exam.api.module.Examination;
import com.github.tangyi.exam.api.module.Subject;
......@@ -26,6 +25,8 @@ import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
......@@ -66,11 +67,8 @@ public class SubjectController extends BaseController {
@ApiImplicitParam(name = "id", value = "题目ID", required = true, dataType = "String", paramType = "path")
public ResponseBean<Subject> subject(@PathVariable String id) {
Subject subject = new Subject();
if (StringUtils.isNotBlank(id)) {
subject.setId(id);
subject = subjectService.get(subject);
}
return new ResponseBean<>(subject);
subject.setId(id);
return new ResponseBean<>(subjectService.get(subject));
}
/**
......@@ -99,6 +97,7 @@ public class SubjectController extends BaseController {
@RequestParam(value = CommonConstant.SORT, required = false, defaultValue = CommonConstant.PAGE_SORT_DEFAULT) String sort,
@RequestParam(value = CommonConstant.ORDER, required = false, defaultValue = CommonConstant.PAGE_ORDER_DEFAULT) String order,
Subject subject) {
subject.setTenantCode(SysUtil.getTenantCode());
return subjectService.findPage(PageUtil.pageInfo(pageNum, pageSize, sort, order), subject);
}
......@@ -111,14 +110,13 @@ public class SubjectController extends BaseController {
* @date 2018/11/10 21:43
*/
@PostMapping
@PreAuthorize("hasAuthority('exam:exam:subject:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PreAuthorize("hasAuthority('exam:exam:subject:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "创建题目", notes = "创建题目")
@ApiImplicitParam(name = "subject", value = "题目实体subject", required = true, dataType = "Subject")
@Log("新增题目")
public ResponseBean<Boolean> addSubject(@RequestBody Subject subject) {
Assert.notNull(subject.getExaminationId(), CommonConstant.IllEGAL_ARGUMENT);
public ResponseBean<Boolean> addSubject(@RequestBody @Valid Subject subject) {
boolean success = false;
subject.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
subject.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
if (subjectService.insert(subject) > 0) {
// 更新考试的题目数
Examination examination = new Examination();
......@@ -139,12 +137,12 @@ public class SubjectController extends BaseController {
* @date 2018/11/10 21:43
*/
@PutMapping
@PreAuthorize("hasAuthority('exam:exam:subject:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PreAuthorize("hasAuthority('exam:exam:subject:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "更新题目信息", notes = "根据题目id更新题目的基本信息")
@ApiImplicitParam(name = "subject", value = "角色实体subject", required = true, dataType = "Subject")
@Log("更新题目")
public ResponseBean<Boolean> updateSubject(@RequestBody Subject subject) {
subject.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
public ResponseBean<Boolean> updateSubject(@RequestBody @Valid Subject subject) {
subject.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
return new ResponseBean<>(subjectService.update(subject) > 0);
}
......@@ -157,7 +155,7 @@ public class SubjectController extends BaseController {
* @date 2018/11/10 21:43
*/
@DeleteMapping("{id}")
@PreAuthorize("hasAuthority('exam:exam:subject:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PreAuthorize("hasAuthority('exam:exam:subject:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "删除题目", notes = "根据ID删除题目")
@ApiImplicitParam(name = "id", value = "题目ID", required = true, paramType = "path")
@Log("删除题目")
......@@ -168,7 +166,7 @@ public class SubjectController extends BaseController {
subject.setId(id);
subject = subjectService.get(subject);
if (subject != null) {
subject.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
subject.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
if (subjectService.delete(subject) > 0) {
// 更新考试的题目数
Examination examination = new Examination();
......@@ -192,7 +190,7 @@ public class SubjectController extends BaseController {
* @date 2018/11/28 12:53
*/
@PostMapping("/export")
@PreAuthorize("hasAuthority('exam:exam:subject:export') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PreAuthorize("hasAuthority('exam:exam:subject:export') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "导出题目", notes = "根据分类id导出题目")
@ApiImplicitParam(name = "subjectDto", value = "题目信息", required = true, dataType = "SubjectDto")
@Log("导出题目")
......@@ -216,6 +214,7 @@ public class SubjectController extends BaseController {
.filter(Objects::nonNull).collect(Collectors.toList());
} else if (StringUtils.isNotEmpty(subjectDto.getExaminationId())) { // 根据考试id导出
Subject subject = new Subject();
subject.setTenantCode(SysUtil.getTenantCode());
subject.setExaminationId(subjectDto.getExaminationId());
subjects = subjectService.findList(subject);
}
......@@ -235,11 +234,11 @@ public class SubjectController extends BaseController {
* @date 2018/11/28 12:59
*/
@RequestMapping("import")
@PreAuthorize("hasAuthority('exam:exam:subject:import') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PreAuthorize("hasAuthority('exam:exam:subject:import') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "导入题目", notes = "导入题目")
@ApiImplicitParam(name = "examinationId", value = "考试ID", required = true, dataType = "String")
@Log("导入题目")
public ResponseBean<Boolean> importSubject(String examinationId, @ApiParam(value = "要上传的文件", required = true) MultipartFile file) {
public ResponseBean<Boolean> importSubject(@NotBlank String examinationId, @ApiParam(value = "要上传的文件", required = true) MultipartFile file) {
boolean success = false;
Assert.notNull(examinationId, CommonConstant.IllEGAL_ARGUMENT);
try {
......@@ -250,7 +249,7 @@ public class SubjectController extends BaseController {
subjectStream.forEach(subject -> {
// 初始化考试ID
if (StringUtils.isBlank(subject.getId())) {
subject.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
subject.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
subject.setExaminationId(examinationId);
subjectService.insert(subject);
}
......@@ -278,7 +277,7 @@ public class SubjectController extends BaseController {
* @date 2018/12/04 9:55
*/
@PostMapping("deleteAll")
@PreAuthorize("hasAuthority('exam:exam:subject:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "', '" + SecurityConstant.ROLE_TEACHER + "')")
@PreAuthorize("hasAuthority('exam:exam:subject:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@ApiOperation(value = "批量删除题目", notes = "根据题目id批量删除题目")
@ApiImplicitParam(name = "subjectDto", value = "题目信息", dataType = "SubjectDto")
@Log("批量删除题目")
......@@ -324,8 +323,8 @@ public class SubjectController extends BaseController {
@ApiImplicitParam(name = "examRecordId", value = "考试记录ID", required = true, dataType = "String"),
@ApiImplicitParam(name = "userId", value = "用户ID", dataType = "String")
})
public ResponseBean<SubjectDto> subjectAnswer(@RequestParam("serialNumber") String serialNumber,
@RequestParam("examRecordId") String examRecordId,
public ResponseBean<SubjectDto> subjectAnswer(@RequestParam("serialNumber") @NotBlank String serialNumber,
@RequestParam("examRecordId") @NotBlank String examRecordId,
@RequestParam(value = "userId", required = false) String userId) {
return new ResponseBean<>(answerService.subjectAnswer(serialNumber, examRecordId, userId));
}
......
package com.github.tangyi.exam.error;
import com.github.tangyi.common.core.model.ResponseBean;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import java.util.List;
import java.util.stream.Collectors;
/**
* 全局异常处理
*
* @author tangyi
* @date 2019/05/25 15:36
*/
@ControllerAdvice
public class CustomGlobalExceptionHandler extends ResponseEntityExceptionHandler {
/**
* 处理参数校验异常
*
* @param ex ex
* @param headers headers
* @param status status
* @param request request
* @return ResponseEntity
*/
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
HttpHeaders headers,
HttpStatus status, WebRequest request) {
// 获取所有异常信息
List<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.toList());
ResponseBean<List<String>> responseBean = new ResponseBean<>(errors);
responseBean.setStatus(status.value());
return new ResponseEntity<>(responseBean, headers, status);
}
}
......@@ -11,5 +11,5 @@ import org.apache.ibatis.annotations.Mapper;
* @date 2018/12/9 14:10
*/
@Mapper
public interface SubjectBankMapper extends CrudMapper<SubjectBank> {
public interface SubjectBankMapper extends CrudMapper<SubjectBank> {
}
......@@ -4,7 +4,6 @@ import com.github.tangyi.common.core.constant.MqConstant;
import com.github.tangyi.common.core.exceptions.CommonException;
import com.github.tangyi.common.core.service.CrudService;
import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.security.utils.SecurityUtil;
import com.github.tangyi.exam.api.constants.ExamRecordConstant;
import com.github.tangyi.exam.api.constants.SubjectConstant;
import com.github.tangyi.exam.api.dto.StartExamDto;
......@@ -127,7 +126,7 @@ public class AnswerService extends CrudService<AnswerMapper, Answer> {
*/
@Transactional
public int save(Answer answer) {
answer.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
answer.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
return super.save(answer);
}
......@@ -143,12 +142,12 @@ public class AnswerService extends CrudService<AnswerMapper, Answer> {
public SubjectDto saveAndNext(Answer answer) {
Answer tempAnswer = this.getAnswer(answer);
if (tempAnswer != null) {
tempAnswer.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
tempAnswer.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
tempAnswer.setAnswer(answer.getAnswer());
tempAnswer.setOptionAnswer(answer.getOptionAnswer());
this.update(tempAnswer);
} else {
answer.setCommonValue(SecurityUtil.getCurrentUsername(), SysUtil.getSysCode());
answer.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
this.insert(answer);
}
return this.subjectAnswer(answer.getSerialNumber(), answer.getExamRecordId(), answer.getUserId());
......@@ -210,7 +209,7 @@ public class AnswerService extends CrudService<AnswerMapper, Answer> {
incorrectAnswer.setSubjectId(tempAnswer.getSubjectId());
incorrectAnswer.setSerialNumber(tempSubject.getSerialNumber());
incorrectAnswer.setUserId(tempAnswer.getUserId());
incorrectAnswer.setIncorrectAnswer(tempAnswer.getAnswer());
incorrectAnswer.setIncorrectAnswer(tempAnswer.getOptionAnswer());
incorrectAnswers.add(incorrectAnswer);
});
});
......@@ -257,10 +256,10 @@ public class AnswerService extends CrudService<AnswerMapper, Answer> {
@Transactional
public boolean submitAsync(Answer answer) {
long start = System.currentTimeMillis();
String currentUsername = SecurityUtil.getCurrentUsername();
String currentUsername = SysUtil.getUser();
answer.setModifier(currentUsername);
ExamRecord examRecord = new ExamRecord();
examRecord.setCommonValue(currentUsername, SysUtil.getSysCode());
examRecord.setCommonValue(currentUsername, SysUtil.getSysCode(), SysUtil.getTenantCode());
examRecord.setId(answer.getExamRecordId());
// 提交时间
examRecord.setEndTime(examRecord.getCreateDate());
......@@ -284,7 +283,7 @@ public class AnswerService extends CrudService<AnswerMapper, Answer> {
@Transactional
public StartExamDto start(ExamRecord examRecord) {
StartExamDto startExamDto = new StartExamDto();
String currentUsername = SecurityUtil.getCurrentUsername(), applicationCode = SysUtil.getSysCode();
String currentUsername = SysUtil.getUser(), applicationCode = SysUtil.getSysCode(), tenantCode = SysUtil.getTenantCode();
// 创建考试记录
if (StringUtils.isEmpty(examRecord.getExaminationId()))
throw new CommonException("参数校验失败,考试id为空!");
......@@ -298,7 +297,7 @@ public class AnswerService extends CrudService<AnswerMapper, Answer> {
examRecord.setExaminationName(examination.getExaminationName());
examRecord.setCourseId(examination.getCourseId());
}
examRecord.setCommonValue(currentUsername, applicationCode);
examRecord.setCommonValue(currentUsername, applicationCode, tenantCode);
examRecord.setStartTime(examRecord.getCreateDate());
// 默认未提交状态
examRecord.setSubmitStatus(ExamRecordConstant.STATUS_NOT_SUBMITTED);
......@@ -322,7 +321,7 @@ public class AnswerService extends CrudService<AnswerMapper, Answer> {
BeanUtils.copyProperties(subject, subjectDto);
// 创建第一题的答题
Answer answer = new Answer();
answer.setCommonValue(currentUsername, applicationCode);
answer.setCommonValue(currentUsername, applicationCode, tenantCode);
answer.setUserId(examRecord.getUserId());
answer.setExaminationId(examRecord.getExaminationId());
answer.setExamRecordId(examRecord.getId());
......
package com.github.tangyi.exam.service;
import com.github.tangyi.common.core.service.CrudService;
import com.github.tangyi.exam.mapper.CourseMapper;
import com.github.tangyi.exam.api.module.Course;
import com.github.tangyi.exam.mapper.CourseMapper;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
......
package com.github.tangyi.exam.service;
import com.github.tangyi.common.core.service.CrudService;
import com.github.tangyi.exam.mapper.IncorrectAnswerMapper;
import com.github.tangyi.exam.api.module.ExamRecord;
import com.github.tangyi.exam.api.module.IncorrectAnswer;
import com.github.tangyi.exam.mapper.IncorrectAnswerMapper;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
......
package com.github.tangyi.exam.service;
import com.github.tangyi.common.core.service.CrudService;
import com.github.tangyi.exam.mapper.KnowledgeMapper;
import com.github.tangyi.exam.api.module.Knowledge;
import com.github.tangyi.exam.mapper.KnowledgeMapper;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
......
package com.github.tangyi.exam.service;
import com.github.tangyi.common.core.service.CrudService;
import com.github.tangyi.exam.mapper.SubjectBankMapper;
import com.github.tangyi.exam.api.module.SubjectBank;
import com.github.tangyi.exam.mapper.SubjectBankMapper;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
......
package com.github.tangyi.exam.service;
import com.github.tangyi.common.core.service.CrudService;
import com.github.tangyi.exam.mapper.SubjectCategoryMapper;
import com.github.tangyi.exam.api.module.SubjectCategory;
import com.github.tangyi.exam.mapper.SubjectCategoryMapper;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
......
......@@ -41,6 +41,7 @@ public class SubjectBankUtil {
map.put("modifyDate", "修改时间");
map.put("delFlag", "删除标记");
map.put("applicationCode", "系统编码");
map.put("tenantCode", "租户标识");
return map;
}
}
......@@ -41,6 +41,7 @@ public class SubjectUtil {
map.put("modifyDate", "修改时间");
map.put("delFlag", "删除标记");
map.put("applicationCode", "系统编码");
map.put("tenantCode", "租户标识");
return map;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="false">
<springProperty scop="context" name="spring.application.name" source="spring.application.name" defaultValue="spring-microservice-exam"/>
<property name="log.path" value="logs/${spring.application.name}" />
<springProperty scop="context" name="spring.application.name" source="spring.application.name"
defaultValue="spring-microservice-exam"/>
<property name="log.path" value="logs/${spring.application.name}"/>
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
......@@ -40,8 +41,8 @@
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="debug" />
<appender-ref ref="error" />
<appender-ref ref="console"/>
<appender-ref ref="debug"/>
<appender-ref ref="error"/>
</root>
</configuration>
\ No newline at end of file
</configuration>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment