diff --git a/pom.xml b/pom.xml index 7def1df..8d0295e 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ 1.8 3.3.0 5.7.20 - 3.0.3 + 2.0.9 @@ -98,8 +98,8 @@ com.github.xiaoymin - knife4j-micro-spring-boot-starter - ${knife4j-micro-spring-boot-starter.version} + knife4j-spring-boot-starter + ${knife4j-spring-boot-starter.version} javax.validation diff --git a/src/main/java/com/jwl/driver/server/config/AuthConfig.java b/src/main/java/com/jwl/driver/server/config/AuthConfig.java new file mode 100644 index 0000000..4895009 --- /dev/null +++ b/src/main/java/com/jwl/driver/server/config/AuthConfig.java @@ -0,0 +1,34 @@ +package com.jwl.driver.server.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 权限配置类 + */ +@ConfigurationProperties(prefix = AuthConfig.PREFIX) +@Component +@Data +public class AuthConfig { + + + public static final String PREFIX = "driver.auth-config"; + + /** + * 约定为若 preAuth 为 true,则所有请求(除不需权限校验url集合noAuthEndPoints外)都需要身份认证 + * 为 false,则所有请求(除需要权限校验url集合needAuthEndPoints外)都不需要身份认证 + */ + private Boolean preAuth; + + /** + * 不需权限校验 url 集合 + */ + private String[] noAuthEndPoints; + + /** + * 必须验证的权限 url 集合 + */ + private String[] needAuthEndPoints; + +} diff --git a/src/main/java/com/jwl/driver/server/config/CorsConfig.java b/src/main/java/com/jwl/driver/server/config/CorsConfig.java index 173928b..9ffdd7d 100644 --- a/src/main/java/com/jwl/driver/server/config/CorsConfig.java +++ b/src/main/java/com/jwl/driver/server/config/CorsConfig.java @@ -26,7 +26,8 @@ public class CorsConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authInterceptor).addPathPatterns("/**").excludePathPatterns("/**/login", -// "/error", + "/error", + "/webjars/**", "/doc.html"); } @@ -56,11 +57,6 @@ public class CorsConfig implements WebMvcConfigurer { registry.addResourceHandler("doc.html") .addResourceLocations("classpath:/META-INF/resources/"); - /** - * 配置swagger-ui显示文档 - */ - registry.addResourceHandler("swagger-ui.html") - .addResourceLocations("classpath:/META-INF/resources/"); /** 公共部分内容 */ registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/"); diff --git a/src/main/java/com/jwl/driver/server/controller/TdSysUserController.java b/src/main/java/com/jwl/driver/server/controller/TdSysUserController.java index 89c32b9..c1e334b 100644 --- a/src/main/java/com/jwl/driver/server/controller/TdSysUserController.java +++ b/src/main/java/com/jwl/driver/server/controller/TdSysUserController.java @@ -8,12 +8,11 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.*; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RestController; + +import java.time.LocalDateTime; /** *

@@ -35,9 +34,17 @@ public class TdSysUserController { @ApiOperation("用户登陆") @PostMapping("/login") - public BaseResponse login(@RequestBody LoginUserDto loginUserDto) throws Exception { + public BaseResponse login(@RequestBody LoginUserDto loginUserDto) { log.info("用户登录======>loginUserDto:{}", loginUserDto); return BaseResponse.success(userService.login(loginUserDto)); } + + @ApiOperation("用户登出") + @GetMapping("/loginOut") + public BaseResponse loginOut(){ + log.info("用户登出======>{}", LocalDateTime.now()); + return BaseResponse.success(userService.loginOut()); + } + } diff --git a/src/main/java/com/jwl/driver/server/dto/SecurityUser.java b/src/main/java/com/jwl/driver/server/dto/SecurityUser.java new file mode 100644 index 0000000..6d7291c --- /dev/null +++ b/src/main/java/com/jwl/driver/server/dto/SecurityUser.java @@ -0,0 +1,32 @@ +package com.jwl.driver.server.dto; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; + + +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +public class SecurityUser implements Serializable { + + /** + * 用户标识 + */ + private Long userId; + + /** + * 用户名 + */ + private String userName; + + + /** + * 用户token + */ + private String token; + + +} diff --git a/src/main/java/com/jwl/driver/server/filter/TokenFilter.java b/src/main/java/com/jwl/driver/server/filter/TokenFilter.java new file mode 100644 index 0000000..5bcf9bb --- /dev/null +++ b/src/main/java/com/jwl/driver/server/filter/TokenFilter.java @@ -0,0 +1,37 @@ +package com.jwl.driver.server.filter; + +import cn.hutool.core.util.StrUtil; +import com.jwl.driver.server.util.TokenThreadUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * 线程保持当前token + */ +@Component +public class TokenFilter extends OncePerRequestFilter { + private static final Logger log = LoggerFactory.getLogger(TokenFilter.class); + + + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String requestTokenHeader = request.getHeader("Authorization"); + if (StrUtil.isBlank(requestTokenHeader)) { + log.warn("Bearer token 为空"); + filterChain.doFilter((ServletRequest) request, (ServletResponse) response); + return; + } + + TokenThreadUtil.setToken(requestTokenHeader); + filterChain.doFilter(request, response); + } +} diff --git a/src/main/java/com/jwl/driver/server/interceptor/AuthInterceptor.java b/src/main/java/com/jwl/driver/server/interceptor/AuthInterceptor.java index a092248..57ee773 100644 --- a/src/main/java/com/jwl/driver/server/interceptor/AuthInterceptor.java +++ b/src/main/java/com/jwl/driver/server/interceptor/AuthInterceptor.java @@ -1,14 +1,23 @@ package com.jwl.driver.server.interceptor; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.BooleanUtil; +import cn.hutool.core.util.StrUtil; +import com.jwl.driver.server.config.AuthConfig; +import com.jwl.driver.server.constant.ErrorCode; +import com.jwl.driver.server.exception.BusinessException; import com.jwl.driver.server.redis.RedisCache; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; +import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.util.ArrayList; import java.util.Objects; /** @@ -18,29 +27,62 @@ import java.util.Objects; */ @Component +@Slf4j public class AuthInterceptor implements HandlerInterceptor { @Autowired private RedisCache redisCache; + @Resource + private AuthConfig authConfig; + + @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String requestURI = request.getRequestURI(); - if (requestURI.contains("doc.html")){ - return true; - } response.setCharacterEncoding("UTF-8"); response.setContentType("text/html;charset=utf-8"); - String token = request.getHeader("token"); - if (StringUtils.isEmpty(token)) { - response.getWriter().print("用户未登录,请登录后操作!"); - return false; + String requestTokenHeader = request.getHeader("Authorization"); + + Boolean preAuth = authConfig.getPreAuth(); + ArrayList noAuthEndPoints = ListUtil.toList(authConfig.getNoAuthEndPoints()); + ArrayList needAuthEndPoints = ListUtil.toList(authConfig.getNeedAuthEndPoints()); + + if (StrUtil.isBlank(requestTokenHeader)) { + log.warn("Authorization token 为空"); } - Object loginStatus = redisCache.getCacheObject(token); - if( Objects.isNull(loginStatus)){ - response.getWriter().print("登陆异常,请查看!"); - return false; + + // preAuth = false,则所有请求(除需要权限校验url集合needAuthEndPoints外)都不需要身份认证 + if (BooleanUtil.isFalse(preAuth)) { + Boolean contains = needAuthEndPoints.contains(requestURI); + // 必须登录的接口 && token为空 + if (contains && StrUtil.isBlank(requestTokenHeader)) { + throw new BusinessException(ErrorCode.AUTH_ERROR, "尚未授权登录"); + } + }else { + // preAuth = true; + // 配置文件无需登录的接口 + Boolean contains = noAuthEndPoints.contains(requestURI); + // 具备排除标志的接口 + Boolean excludeFlag = Boolean.FALSE; + for (String exclude : needAuthEndPoints) { + if (requestURI.contains(exclude)) { + excludeFlag = Boolean.TRUE; + break; + } + } + if (!contains && !excludeFlag && StrUtil.isBlank(requestTokenHeader)) { + throw new BusinessException(ErrorCode.AUTH_ERROR, "尚未授权登录"); + } } + + if (StrUtil.isNotBlank(requestTokenHeader)){ + Object loginStatus = redisCache.getCacheObject(requestTokenHeader); + if( Objects.isNull(loginStatus)){ + throw new BusinessException(ErrorCode.AUTH_ERROR, "登陆已过期"); + } + } + return true; } diff --git a/src/main/java/com/jwl/driver/server/service/ITdSysUserService.java b/src/main/java/com/jwl/driver/server/service/ITdSysUserService.java index d4b3f44..0b32459 100644 --- a/src/main/java/com/jwl/driver/server/service/ITdSysUserService.java +++ b/src/main/java/com/jwl/driver/server/service/ITdSysUserService.java @@ -15,5 +15,16 @@ import com.jwl.driver.server.vo.LoginUserVo; */ public interface ITdSysUserService extends IService { + /** + * 用户登陆 + * @param loginUserDto + * @return + */ LoginUserVo login(LoginUserDto loginUserDto); + + /** + * 用户登出 + * @return + */ + Boolean loginOut(); } diff --git a/src/main/java/com/jwl/driver/server/service/impl/TdSysUserServiceImpl.java b/src/main/java/com/jwl/driver/server/service/impl/TdSysUserServiceImpl.java index a07e09c..4bde82a 100644 --- a/src/main/java/com/jwl/driver/server/service/impl/TdSysUserServiceImpl.java +++ b/src/main/java/com/jwl/driver/server/service/impl/TdSysUserServiceImpl.java @@ -1,17 +1,22 @@ package com.jwl.driver.server.service.impl; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.jwl.driver.server.dto.LoginUserDto; import com.jwl.driver.server.entity.TdSysUser; +import com.jwl.driver.server.exception.BusinessException; import com.jwl.driver.server.mapper.TdSysUserMapper; import com.jwl.driver.server.redis.RedisCache; import com.jwl.driver.server.service.ITdSysUserService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.jwl.driver.server.util.TokenThreadUtil; import com.jwl.driver.server.vo.LoginUserVo; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; import java.util.UUID; /** @@ -29,13 +34,24 @@ public class TdSysUserServiceImpl extends ServiceImpl cond = new LambdaQueryWrapper() .eq(TdSysUser::getPhone,"18255439337"); + //用户不存在则直接注册登陆 TdSysUser tdSysUser = this.baseMapper.selectOne(cond); if (tdSysUser == null){ - //创建用户 + tdSysUser = new TdSysUser() + .setUserName("车友") + .setAvatar("") + .setPhone(loginUserDto.getPhone()) + .setCreateTime(LocalDateTime.now()); + int insert = this.getBaseMapper().insert(tdSysUser); + if (insert != 1){ + throw new BusinessException("用户注册异常"); + } } String token = UUID.randomUUID().toString(); @@ -45,8 +61,14 @@ public class TdSysUserServiceImpl extends ServiceImplgetBean(RedisCache.class); + + + /** + * 获取当前登录人信息 + * @return + */ + public static SecurityUser getLoginUser() { + String token = TokenThreadUtil.getToken(); + if (StrUtil.isBlank(token)) + throw new BusinessException(ErrorCode.AUTH_ERROR, "尚未登录"); + SecurityUser securityUser = null; + TdSysUser tdSysUser = redisCache.getCacheObject(token); + + if (Objects.isNull(securityUser)){ + throw new BusinessException(ErrorCode.AUTH_ERROR, "登录信息已失效"); + } + BeanUtils.copyProperties(tdSysUser,securityUser); + securityUser.setToken(token); + + return securityUser; + } + + /** + * 获取当前登录人用户编号 + * @return + */ + public static Long getUserId() { + Long userId = null; + SecurityUser loginUser = SecurityUtil.getLoginUser(); + if (null != loginUser) { + userId = loginUser.getUserId(); + } + + return userId; + } + + +} diff --git a/src/main/java/com/jwl/driver/server/util/SpringAsyncUtil.java b/src/main/java/com/jwl/driver/server/util/SpringAsyncUtil.java new file mode 100644 index 0000000..36d1aaf --- /dev/null +++ b/src/main/java/com/jwl/driver/server/util/SpringAsyncUtil.java @@ -0,0 +1,62 @@ +package com.jwl.driver.server.util; + + +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; + +public final class SpringAsyncUtil implements BeanFactoryPostProcessor, ApplicationContextAware { + private static ConfigurableListableBeanFactory beanFactory; + + private static ApplicationContext applicationContext; + + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + SpringAsyncUtil.beanFactory = beanFactory; + } + + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + SpringAsyncUtil.applicationContext = applicationContext; + } + + public static T getBean(String name) throws BeansException { + return (T)beanFactory.getBean(name); + } + + public static T getBean(Class clz) throws BeansException { + T result = (T)beanFactory.getBean(clz); + return result; + } + + public static boolean containsBean(String name) { + return beanFactory.containsBean(name); + } + + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return beanFactory.isSingleton(name); + } + + public static Class getType(String name) throws NoSuchBeanDefinitionException { + return beanFactory.getType(name); + } + + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { + return beanFactory.getAliases(name); + } + + public static T getAopProxy(T invoker) { + return (T)AopContext.currentProxy(); + } + + public static String[] getActiveProfiles() { + return applicationContext.getEnvironment().getActiveProfiles(); + } + + public static String getActiveProfile() { + String[] activeProfiles = getActiveProfiles(); + return (activeProfiles.length > 0) ? activeProfiles[0] : null; + } +} diff --git a/src/main/java/com/jwl/driver/server/util/TokenThreadUtil.java b/src/main/java/com/jwl/driver/server/util/TokenThreadUtil.java new file mode 100644 index 0000000..72eecb9 --- /dev/null +++ b/src/main/java/com/jwl/driver/server/util/TokenThreadUtil.java @@ -0,0 +1,23 @@ +package com.jwl.driver.server.util; + +import cn.hutool.core.util.StrUtil; + +/** + * 线程变量-token + */ +public class TokenThreadUtil { + private static ThreadLocal threadLocal = new ThreadLocal<>(); + + public static void setToken(String token) { + if (StrUtil.isNotBlank(token)) + threadLocal.set(token); + } + + public static String getToken() { + return threadLocal.get(); + } + + public static void remove() { + threadLocal.remove(); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index d84c9b4..0b533ea 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,7 +1,7 @@ server: port: 8888 -# servlet: -# context-path: '/driver-api' + servlet: + context-path: '/driver-api' spring: application: @@ -29,3 +29,18 @@ pagehelper: knife4j: # 是否开启增强模式 enable: true + +# 是否需要校验token +driver: + auth-config: + # 约定为若 preAuth 为 true,则所有请求(除不需权限校验url集合noAuthEndPoints外)都需要身份认证 + # 为 false,则所有请求(除需要权限校验url集合needAuthEndPoints外)都不需要身份认证 + preAuth: true + # 不需权限校验url集合 + noAuthEndPoints: + - /driver-api/v2/api-docs + - /driver-api/swagger-resources + - /driver-api/favicon.ico + + # 需要权限校验url集合 + needAuthEndPoints: