mirror of
https://gitee.com/lab1024/smart-admin.git
synced 2025-11-14 06:33:47 +08:00
add sa-token
This commit is contained in:
@@ -97,7 +97,7 @@ public abstract class AbstractInterceptor implements HandlerInterceptor {
|
||||
if (StringUtils.isNotBlank(xAccessToken)) {
|
||||
RequestUser requestUser = userFunction().apply(xAccessToken);
|
||||
if (requestUser != null) {
|
||||
SmartRequestUtil.setRequestUser(requestUser);
|
||||
// SmartRequestUtil.setRequestUser(requestUser);
|
||||
}
|
||||
// 有token 无需登录
|
||||
if (null != noNeedLogin) {
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
package net.lab1024.sa.common.common.security;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Spring Security
|
||||
*
|
||||
* @Author 1024创新实验室-主任: 卓大
|
||||
* @Date 2021/8/3 17:50
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net )
|
||||
*/
|
||||
public abstract class AbstractSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||
@Autowired
|
||||
private CorsFilter corsFilter;
|
||||
|
||||
@Autowired
|
||||
private List<String> noNeedLoginUrlList;
|
||||
|
||||
@Autowired
|
||||
private List<String> ignoreUrlList;
|
||||
|
||||
/**
|
||||
* Token获取用户信息
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract BiFunction<String, HttpServletRequest, UserDetails> userFunction();
|
||||
|
||||
/**
|
||||
* 需要认证的url集合
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract String[] getAuthenticatedUrlPatterns();
|
||||
|
||||
/**
|
||||
* 不需要登录的url集合
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected String[] getNoNeedLoginUrl() {
|
||||
return noNeedLoginUrlList.toArray(new String[noNeedLoginUrlList.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 忽略的url集合
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected String[] getIgnoreUrlList() {
|
||||
return ignoreUrlList.toArray(new String[ignoreUrlList.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity httpSecurity) throws Exception {
|
||||
httpSecurity
|
||||
// CSRF禁用,因为不使用session
|
||||
.csrf().disable()
|
||||
// 认证失败处理类
|
||||
.exceptionHandling().authenticationEntryPoint(new SecurityAuthenticationFailHandler()).and()
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
|
||||
// 过滤请求
|
||||
.authorizeRequests()
|
||||
//忽略的url
|
||||
.antMatchers(this.getIgnoreUrlList()).permitAll()
|
||||
// 不需要登陆的url
|
||||
.antMatchers(this.getNoNeedLoginUrl()).permitAll()
|
||||
//需要校验权限的url
|
||||
.antMatchers(getAuthenticatedUrlPatterns()).authenticated();
|
||||
|
||||
// token filter 进行校验
|
||||
httpSecurity.addFilterBefore(new SecurityTokenFilter(this.userFunction()), UsernamePasswordAuthenticationFilter.class);
|
||||
httpSecurity.addFilterBefore(corsFilter, SecurityTokenFilter.class);
|
||||
// 禁用spring security 使用 X-Frame-Options防止网页被Frame
|
||||
httpSecurity.headers().frameOptions().disable();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package net.lab1024.sa.common.common.security;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import net.lab1024.sa.common.common.code.ErrorCode;
|
||||
import net.lab1024.sa.common.common.code.UserErrorCode;
|
||||
import net.lab1024.sa.common.common.domain.ResponseDTO;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 登录认证失败处理
|
||||
*
|
||||
* @Author 1024创新实验室: 罗伊
|
||||
* @Date 2022-08-26 20:21:10
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net )
|
||||
*/
|
||||
public class SecurityAuthenticationFailHandler implements AuthenticationEntryPoint {
|
||||
|
||||
@Override
|
||||
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException {
|
||||
this.outputResult(response, UserErrorCode.LOGIN_STATE_INVALID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 输出
|
||||
*
|
||||
* @param response
|
||||
* @param errorCode
|
||||
* @throws IOException
|
||||
*/
|
||||
private void outputResult(HttpServletResponse response, ErrorCode errorCode) throws IOException {
|
||||
String msg = JSONObject.toJSONString(ResponseDTO.error(errorCode));
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.getWriter().write(msg);
|
||||
response.flushBuffer();
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package net.lab1024.sa.common.common.security;
|
||||
|
||||
import net.lab1024.sa.common.common.annoation.SaAuth;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.prepost.PreInvocationAttribute;
|
||||
import org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource;
|
||||
import org.springframework.security.access.prepost.PrePostInvocationAttributeFactory;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 此类用于默认给所有接口添加权限 @saAuth.checkPermission('%s')
|
||||
* %s 为类名.方法名
|
||||
* 和使用@PreAuthorize("@saAuth.checkPermission('%s')") 效果一致
|
||||
* 避免所有接口都添加一遍 减轻工作量
|
||||
*
|
||||
* @Author 1024创新实验室: 罗伊
|
||||
* @Date 2021-08-30 23:08
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net )
|
||||
*/
|
||||
public class SecurityMethodSource extends PrePostAnnotationSecurityMetadataSource {
|
||||
|
||||
|
||||
private static String EXPRESSION_FORMAT = "@%s.checkPermission('%s')";
|
||||
|
||||
private final PrePostInvocationAttributeFactory attributeFactory;
|
||||
|
||||
private String beanName;
|
||||
|
||||
|
||||
public SecurityMethodSource(PrePostInvocationAttributeFactory attributeFactory, String beanName) {
|
||||
super(attributeFactory);
|
||||
this.attributeFactory = attributeFactory;
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
|
||||
//如果不存在SaAuth采用security认证模式
|
||||
SaAuth saAuth = method.getAnnotation(SaAuth.class);
|
||||
if (saAuth == null) {
|
||||
return super.getAttributes(method, targetClass);
|
||||
}
|
||||
|
||||
//存在添加以URL为权限字符串的校验模式
|
||||
ArrayList<ConfigAttribute> configAttributes = new ArrayList(1);
|
||||
String classFullName = targetClass.getName();
|
||||
String methodName = method.getName();
|
||||
String[] classNameArray = StringUtils.split(classFullName, "\\.");
|
||||
String controllerName = classNameArray[classNameArray.length - 1];
|
||||
String privilegeName = controllerName + "." + methodName;
|
||||
String preAuthorizeAttribute = String.format(EXPRESSION_FORMAT, beanName, privilegeName);
|
||||
PreInvocationAttribute pre = this.attributeFactory.createPreInvocationAttribute(null, null, preAuthorizeAttribute);
|
||||
if (pre != null) {
|
||||
configAttributes.add(pre);
|
||||
}
|
||||
return configAttributes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package net.lab1024.sa.common.common.security;
|
||||
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 校验权限
|
||||
*
|
||||
* @Author 1024创新实验室: 罗伊
|
||||
* @Date 2022/5/12 21:50
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net )
|
||||
*/
|
||||
public abstract class SecurityPermissionCheckService {
|
||||
|
||||
|
||||
/**
|
||||
* 校验是否有权限
|
||||
*
|
||||
* @param permission
|
||||
* @return
|
||||
*/
|
||||
public boolean checkPermission(String permission) {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication == null) {
|
||||
return false;
|
||||
}
|
||||
return checkPermission(authentication, permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验是否有权限
|
||||
*
|
||||
* @param authentication
|
||||
* @param permission
|
||||
* @return
|
||||
*/
|
||||
public abstract boolean checkPermission(Authentication authentication, String permission);
|
||||
|
||||
/**
|
||||
* 判断
|
||||
*
|
||||
* @param userDetails
|
||||
* @param permissionStr
|
||||
* @return
|
||||
*/
|
||||
protected boolean permissionJudge(UserDetails userDetails, String permissionStr) {
|
||||
if (CollectionUtils.isEmpty(userDetails.getAuthorities())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(permissionStr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String[] permissionArray = permissionStr.split(",");
|
||||
for (String permission : permissionArray) {
|
||||
if(userDetails.getAuthorities().contains(new SimpleGrantedAuthority(permission))){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package net.lab1024.sa.common.common.security;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.lab1024.sa.common.common.constant.RequestHeaderConst;
|
||||
import net.lab1024.sa.common.common.domain.RequestUser;
|
||||
import net.lab1024.sa.common.common.util.SmartRequestUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 注意此处不能 加入@Component,否则对应ignoreUrl的相关请求 将会进入此Filter,并会覆盖CorsFilter
|
||||
*
|
||||
* @Author 1024创新实验室: 罗伊
|
||||
* @Date 2022/5/12 21:50
|
||||
* @Wechat zhuoda1024
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net )
|
||||
*/
|
||||
@Slf4j
|
||||
public class SecurityTokenFilter extends OncePerRequestFilter {
|
||||
|
||||
private BiFunction<String,HttpServletRequest, UserDetails> userFunction;
|
||||
|
||||
public SecurityTokenFilter(BiFunction<String,HttpServletRequest, UserDetails> userFunction) {
|
||||
this.userFunction = userFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
|
||||
throws ServletException, IOException {
|
||||
//需要做token校验, 消息头的token优先于请求query参数的token
|
||||
String xHeaderToken = request.getHeader(RequestHeaderConst.TOKEN);
|
||||
String xRequestToken = request.getParameter(RequestHeaderConst.TOKEN);
|
||||
String xAccessToken = null != xHeaderToken ? xHeaderToken : xRequestToken;
|
||||
if (StringUtils.isBlank(xAccessToken)) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
//清理spring security
|
||||
SecurityContextHolder.clearContext();
|
||||
|
||||
UserDetails loginUserDetail = userFunction.apply(xAccessToken,request);
|
||||
if (null != loginUserDetail) {
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUserDetail, null, loginUserDetail.getAuthorities());
|
||||
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
|
||||
SmartRequestUtil.setRequestUser((RequestUser) loginUserDetail);
|
||||
}
|
||||
// 若未给予spring security上下文用户授权 则会授权失败 进入AuthenticationEntryPointImpl
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
@@ -14,24 +14,18 @@ import net.lab1024.sa.common.common.domain.RequestUser;
|
||||
*/
|
||||
@Slf4j
|
||||
public class SmartRequestUtil {
|
||||
|
||||
private static final ThreadLocal<RequestUser> requestThreadLocal = new ThreadLocal<>();
|
||||
|
||||
public static void setRequestUser(RequestUser requestUser) {
|
||||
requestThreadLocal.set(requestUser);
|
||||
public static RequestUser getUser() {
|
||||
// TODO listen
|
||||
return null;
|
||||
}
|
||||
|
||||
public static RequestUser getRequestUser() {
|
||||
return requestThreadLocal.get();
|
||||
}
|
||||
|
||||
public static Long getRequestUserId() {
|
||||
RequestUser requestUser = getRequestUser();
|
||||
public static Long getUserId() {
|
||||
RequestUser requestUser = getUser();
|
||||
return null == requestUser ? null : requestUser.getUserId();
|
||||
}
|
||||
|
||||
public static void remove() {
|
||||
requestThreadLocal.remove();
|
||||
// TODO listen
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ public class RepeatSubmitConfig {
|
||||
* @return
|
||||
*/
|
||||
private String ticket(String servletPath) {
|
||||
Long userId = SmartRequestUtil.getRequestUserId();
|
||||
Long userId = SmartRequestUtil.getUserId();
|
||||
if (null == userId) {
|
||||
return StringConst.EMPTY;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import net.lab1024.sa.common.common.exception.BusinessException;
|
||||
import org.springframework.beans.TypeMismatchException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
@@ -77,15 +76,6 @@ public class GlobalExceptionHandler {
|
||||
return ResponseDTO.error(UserErrorCode.PARAM_ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限异常
|
||||
*/
|
||||
@ResponseBody
|
||||
@ExceptionHandler({AccessDeniedException.class})
|
||||
public ResponseDTO<?> permissionExceptionHandler(AccessDeniedException e) {
|
||||
return ResponseDTO.error(UserErrorCode.NO_PERMISSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务异常
|
||||
*/
|
||||
|
||||
@@ -157,7 +157,7 @@ public class DataTracerService {
|
||||
* 保存数据变动记录
|
||||
*/
|
||||
public void addTrace(DataTracerForm tracerForm) {
|
||||
RequestUser requestUser = SmartRequestUtil.getRequestUser();
|
||||
RequestUser requestUser = SmartRequestUtil.getUser();
|
||||
this.addTrace(tracerForm, requestUser);
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ public class DataTracerService {
|
||||
* 批量保存数据变动记录
|
||||
*/
|
||||
public void addTraceList(List<DataTracerForm> tracerFormList) {
|
||||
RequestUser requestUser = SmartRequestUtil.getRequestUser();
|
||||
RequestUser requestUser = SmartRequestUtil.getUser();
|
||||
this.addTraceList(tracerFormList, requestUser);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ import net.lab1024.sa.common.module.support.feedback.domain.FeedbackAddForm;
|
||||
import net.lab1024.sa.common.module.support.feedback.domain.FeedbackQueryForm;
|
||||
import net.lab1024.sa.common.module.support.feedback.domain.FeedbackVO;
|
||||
import net.lab1024.sa.common.module.support.feedback.service.FeedbackService;
|
||||
import net.lab1024.sa.common.module.support.repeatsubmit.annoation.RepeatSubmit;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@@ -47,7 +46,7 @@ public class FeedbackController extends SupportBaseController {
|
||||
@ApiOperation("意见反馈-新增 @author 开云")
|
||||
@PostMapping("/feedback/add")
|
||||
public ResponseDTO<String> add(@RequestBody @Valid FeedbackAddForm addForm) {
|
||||
RequestUser employee = SmartRequestUtil.getRequestUser();
|
||||
RequestUser employee = SmartRequestUtil.getUser();
|
||||
return feedbackService.add(addForm, employee);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,17 +5,13 @@ import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import net.lab1024.sa.common.common.constant.RequestHeaderConst;
|
||||
import net.lab1024.sa.common.common.controller.SupportBaseController;
|
||||
import net.lab1024.sa.common.common.domain.PageResult;
|
||||
import net.lab1024.sa.common.common.domain.RequestUser;
|
||||
import net.lab1024.sa.common.common.domain.ResponseDTO;
|
||||
import net.lab1024.sa.common.common.util.SmartRequestUtil;
|
||||
import net.lab1024.sa.common.constant.SwaggerTagConst;
|
||||
import net.lab1024.sa.common.module.support.file.domain.form.FileQueryForm;
|
||||
import net.lab1024.sa.common.module.support.file.constant.FileFolderTypeEnum;
|
||||
import net.lab1024.sa.common.module.support.file.domain.form.FileUrlUploadForm;
|
||||
import net.lab1024.sa.common.module.support.file.domain.vo.FileUploadVO;
|
||||
import net.lab1024.sa.common.module.support.file.domain.vo.FileVO;
|
||||
import net.lab1024.sa.common.module.support.repeatsubmit.annoation.RepeatSubmit;
|
||||
import net.lab1024.sa.common.module.support.file.constant.FileFolderTypeEnum;
|
||||
import net.lab1024.sa.common.module.support.file.service.FileService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@@ -46,14 +42,14 @@ public class FileController extends SupportBaseController {
|
||||
@PostMapping("/file/upload")
|
||||
public ResponseDTO<FileUploadVO> upload(@RequestParam MultipartFile file,
|
||||
@RequestParam Integer folder) {
|
||||
RequestUser requestUser = SmartRequestUtil.getRequestUser();
|
||||
RequestUser requestUser = SmartRequestUtil.getUser();
|
||||
return fileService.fileUpload(file, folder, requestUser);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "文件上传,通过url上传 @author 胡克", notes = FileFolderTypeEnum.INFO)
|
||||
@PostMapping("/file/upload/url")
|
||||
public ResponseDTO<FileUploadVO> uploadByUrl(@RequestBody @Valid FileUrlUploadForm uploadForm) {
|
||||
RequestUser requestUser = SmartRequestUtil.getRequestUser();
|
||||
RequestUser requestUser = SmartRequestUtil.getUser();
|
||||
return fileService.fileUpload(uploadForm,requestUser);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ public class HelpDocController extends SupportBaseController {
|
||||
@RepeatSubmit
|
||||
public ResponseDTO<HelpDocDetailVO> view(@PathVariable Long helpDocId, HttpServletRequest request) {
|
||||
return helpDocUserService.view(
|
||||
SmartRequestUtil.getRequestUser(),
|
||||
SmartRequestUtil.getUser(),
|
||||
helpDocId);
|
||||
}
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ public abstract class OperateLogAspect {
|
||||
return;
|
||||
}
|
||||
//设置用户信息
|
||||
RequestUser user = SmartRequestUtil.getRequestUser();
|
||||
RequestUser user = SmartRequestUtil.getUser();
|
||||
if (user == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -33,19 +33,19 @@ public class TableColumnController extends SupportBaseController {
|
||||
@PostMapping("/tableColumn/update")
|
||||
@RepeatSubmit
|
||||
public ResponseDTO<String> updateTableColumn(@RequestBody @Valid TableColumnUpdateForm updateForm) {
|
||||
return tableColumnService.updateTableColumns(SmartRequestUtil.getRequestUser(), updateForm);
|
||||
return tableColumnService.updateTableColumns(SmartRequestUtil.getUser(), updateForm);
|
||||
}
|
||||
|
||||
@ApiOperation("恢复默认(删除) @author 卓大")
|
||||
@GetMapping("/tableColumn/delete/{tableId}")
|
||||
@RepeatSubmit
|
||||
public ResponseDTO<String> deleteTableColumn(@PathVariable Integer tableId) {
|
||||
return tableColumnService.deleteTableColumn(SmartRequestUtil.getRequestUser(), tableId);
|
||||
return tableColumnService.deleteTableColumn(SmartRequestUtil.getUser(), tableId);
|
||||
}
|
||||
|
||||
@ApiOperation("查询表格列 @author 卓大")
|
||||
@GetMapping("/tableColumn/getColumns/{tableId}")
|
||||
public ResponseDTO<String> getColumns(@PathVariable Integer tableId) {
|
||||
return ResponseDTO.ok(tableColumnService.getTableColumns(SmartRequestUtil.getRequestUser(), tableId));
|
||||
return ResponseDTO.ok(tableColumnService.getTableColumns(SmartRequestUtil.getUser(), tableId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package net.lab1024.sa.common.module.support.token;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import net.lab1024.sa.common.common.enumeration.BaseEnum;
|
||||
|
||||
/**
|
||||
@@ -11,6 +13,8 @@ import net.lab1024.sa.common.common.enumeration.BaseEnum;
|
||||
* @Email lab1024@163.com
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net )
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum LoginDeviceEnum implements BaseEnum {
|
||||
|
||||
PC(1, "电脑端"),
|
||||
@@ -21,23 +25,9 @@ public enum LoginDeviceEnum implements BaseEnum {
|
||||
|
||||
H5(4, "H5"),
|
||||
|
||||
WEIXIN_MP(5, "微信小程序");
|
||||
WX_MP(5, "微信小程序");
|
||||
|
||||
LoginDeviceEnum(Integer value, String desc) {
|
||||
this.value = value;
|
||||
this.desc = desc;
|
||||
}
|
||||
private final Integer value;
|
||||
|
||||
private Integer value;
|
||||
private String desc;
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
private final String desc;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,11 @@
|
||||
package net.lab1024.sa.common.module.support.token;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.JwtBuilder;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import net.lab1024.sa.common.common.enumeration.UserTypeEnum;
|
||||
import net.lab1024.sa.common.constant.RedisKeyConst;
|
||||
import net.lab1024.sa.common.module.support.redis.RedisService;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 与用户token的相关的服务
|
||||
@@ -27,194 +17,59 @@ import java.util.Map;
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net )
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class TokenService {
|
||||
private static final long HOUR_TIME_MILLI = 60 * 60 * 1000;
|
||||
|
||||
@Value("${token.key}")
|
||||
private String tokenKey;
|
||||
private static final String EXTRA_KEY_USER_NAME = "userName";
|
||||
|
||||
@Value("${token.expire-day}")
|
||||
private Integer tokenExpire;
|
||||
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
private static final String EXTRA_KEY_USER_TYPE = "userType";
|
||||
|
||||
/**
|
||||
* 生成Token,并存入redis
|
||||
* 生成Token
|
||||
*
|
||||
* @param userId
|
||||
* @param userName
|
||||
* @param userTypeEnum
|
||||
* @param loginDeviceEnum
|
||||
* @param superPasswordFlag 特殊万能密码标识
|
||||
* @return
|
||||
*/
|
||||
public String generateToken(Long userId, String userName, UserTypeEnum userTypeEnum, LoginDeviceEnum loginDeviceEnum, Boolean superPasswordFlag) {
|
||||
long nowTimeMilli = System.currentTimeMillis();
|
||||
Claims jwtClaims = Jwts.claims();
|
||||
jwtClaims.put(JwtConst.CLAIM_ID_KEY, userId);
|
||||
jwtClaims.put(JwtConst.CLAIM_NAME_KEY, userName);
|
||||
jwtClaims.put(JwtConst.CLAIM_USER_TYPE_KEY, userTypeEnum.getValue());
|
||||
jwtClaims.put(JwtConst.CLAIM_DEVICE_KEY, loginDeviceEnum.getValue());
|
||||
jwtClaims.put(JwtConst.CLAIM_SUPER_PASSWORD_FLAG, superPasswordFlag);
|
||||
JwtBuilder jwtBuilder = Jwts.builder()
|
||||
.setClaims(jwtClaims)
|
||||
.setIssuedAt(new Date(nowTimeMilli))
|
||||
.signWith(SignatureAlgorithm.HS512, tokenKey);
|
||||
public String generateToken(Long userId,
|
||||
String userName,
|
||||
UserTypeEnum userTypeEnum,
|
||||
LoginDeviceEnum loginDeviceEnum) {
|
||||
|
||||
// 如果是万能密码,则不需要记录到redis中;万能密码最多半个小时有效期
|
||||
if (superPasswordFlag) {
|
||||
jwtBuilder.setExpiration(new Date(nowTimeMilli + (HOUR_TIME_MILLI / 2)));
|
||||
return jwtBuilder.compact();
|
||||
}
|
||||
/**
|
||||
* 设置登录模式参数
|
||||
* 具体参数 @see SaLoginModel 属性
|
||||
* 已经写的挺清楚的了
|
||||
*/
|
||||
SaLoginModel loginModel = new SaLoginModel();
|
||||
// 此次登录的客户端设备类型, 用于[同端互斥登录]时指定此次登录的设备类型
|
||||
loginModel.setDevice(String.valueOf(loginDeviceEnum.getDesc()));
|
||||
// 扩展参数
|
||||
loginModel.setExtra(EXTRA_KEY_USER_NAME, userName);
|
||||
loginModel.setExtra(EXTRA_KEY_USER_TYPE, userTypeEnum.getValue());
|
||||
|
||||
jwtBuilder.setExpiration(new Date(nowTimeMilli + tokenExpire * 24 * HOUR_TIME_MILLI));
|
||||
String token = jwtBuilder.compact();
|
||||
String redisKey = this.generateTokenRedisKey(userId, userTypeEnum.getValue(), loginDeviceEnum.getValue());
|
||||
redisService.set(redisKey, token, tokenExpire * 24 * 3600);
|
||||
return token;
|
||||
String loginId = generateLoginId(userId, userTypeEnum);
|
||||
StpUtil.login(loginId, loginModel);
|
||||
return StpUtil.getTokenValue();
|
||||
}
|
||||
|
||||
public static String generateLoginId(Long userId, UserTypeEnum userType) {
|
||||
return userType.getValue() + "_" + userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成登录信息: 含设备信息
|
||||
*
|
||||
* @param userId
|
||||
* @param device
|
||||
* @return
|
||||
* 退出登录 注销
|
||||
*/
|
||||
private String generateTokenRedisKey(Long userId, Integer userType, Integer device) {
|
||||
String userKey = userType + "_" + userId + "_" + device;
|
||||
return redisService.generateRedisKey(RedisKeyConst.Support.TOKEN, userKey);
|
||||
public void removeToken() {
|
||||
StpUtil.logout();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 强制移除 此用户各端的登录信息
|
||||
*
|
||||
* @param token
|
||||
*/
|
||||
public void removeToken(String token) {
|
||||
Map<String, Object> tokenData = this.decryptTokenData(token);
|
||||
if (MapUtils.isEmpty(tokenData)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//特殊账号
|
||||
if (tokenData.get(JwtConst.CLAIM_SUPER_PASSWORD_FLAG) != null) {
|
||||
try {
|
||||
Boolean superPasswordFlag = Boolean.valueOf(tokenData.get(JwtConst.CLAIM_SUPER_PASSWORD_FLAG).toString());
|
||||
if (superPasswordFlag) {
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
boolean isValid = this.checkRedisToken(tokenData, token);
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
Long userId = Long.valueOf(tokenData.get(JwtConst.CLAIM_ID_KEY).toString());
|
||||
Integer userType = Integer.valueOf(tokenData.get(JwtConst.CLAIM_USER_TYPE_KEY).toString());
|
||||
Integer device = Integer.valueOf(tokenData.get(JwtConst.CLAIM_DEVICE_KEY).toString());
|
||||
|
||||
String redisKey = this.generateTokenRedisKey(userId, userType, device);
|
||||
redisService.delete(redisKey);
|
||||
public void removeToken(Long userId, UserTypeEnum userType) {
|
||||
StpUtil.logout(generateLoginId(userId, userType));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析并校验token信息 获取 userId
|
||||
*
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
public Long getUserIdAndValidateToken(String token) {
|
||||
Map<String, Object> parseJwtData = this.decryptTokenData(token);
|
||||
boolean isValid = this.checkRedisToken(parseJwtData, token);
|
||||
if (!isValid) {
|
||||
return null;
|
||||
}
|
||||
Long userId = Long.valueOf(parseJwtData.get(JwtConst.CLAIM_ID_KEY).toString());
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密和解析token
|
||||
*
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
private Map<String, Object> decryptTokenData(String token) {
|
||||
try {
|
||||
return Jwts.parser()
|
||||
.setSigningKey(tokenKey)
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验token是否有效
|
||||
*
|
||||
* @param token
|
||||
* @return
|
||||
*/
|
||||
private boolean checkRedisToken(Map<String, Object> parseJwtData, String token) {
|
||||
if (MapUtils.isEmpty(parseJwtData)) {
|
||||
return false;
|
||||
}
|
||||
//特殊账号
|
||||
if (parseJwtData.get(JwtConst.CLAIM_SUPER_PASSWORD_FLAG) != null) {
|
||||
try {
|
||||
Boolean superPasswordFlag = Boolean.valueOf(parseJwtData.get(JwtConst.CLAIM_SUPER_PASSWORD_FLAG).toString());
|
||||
if (superPasswordFlag) {
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Long userId = null;
|
||||
Integer userType = null, device = null;
|
||||
|
||||
if (null != parseJwtData.get(JwtConst.CLAIM_ID_KEY)) {
|
||||
userId = NumberUtils.toLong(parseJwtData.get(JwtConst.CLAIM_ID_KEY).toString(), -1);
|
||||
userId = userId == -1 ? null : userId;
|
||||
}
|
||||
|
||||
if (null != parseJwtData.get(JwtConst.CLAIM_USER_TYPE_KEY)) {
|
||||
userType = NumberUtils.toInt(parseJwtData.get(JwtConst.CLAIM_USER_TYPE_KEY).toString(), -1);
|
||||
userType = userType == -1 ? null : userType;
|
||||
}
|
||||
|
||||
if (null != parseJwtData.get(JwtConst.CLAIM_DEVICE_KEY)) {
|
||||
device = NumberUtils.toInt(parseJwtData.get(JwtConst.CLAIM_DEVICE_KEY).toString(), -1);
|
||||
device = device == -1 ? null : device;
|
||||
}
|
||||
|
||||
if (userId == null || userType == null || device == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String redisKey = this.generateTokenRedisKey(userId, userType, device);
|
||||
String redisToken = redisService.get(redisKey);
|
||||
return token.equals(redisToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量移除用户所有设备的token
|
||||
*/
|
||||
public void batchRemoveRedisToken(Long userId, UserTypeEnum userTypeEnum) {
|
||||
for (LoginDeviceEnum device : LoginDeviceEnum.values()) {
|
||||
redisService.delete(this.generateTokenRedisKey(userId, userTypeEnum.getValue(), device.getValue()));
|
||||
}
|
||||
public void removeToken(List<Long> userIdList, UserTypeEnum userType) {
|
||||
userIdList.forEach(id -> StpUtil.logout(generateLoginId(id, userType)));
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ spring:
|
||||
datasource:
|
||||
url: jdbc:p6spy:mysql://127.0.0.1:3306/smart_admin_v2?autoReconnect=true&useServerPreparedStmts=false&rewriteBatchedStatements=true&characterEncoding=UTF-8&useSSL=false&allowMultiQueries=true&serverTimezone=Asia/Shanghai
|
||||
username: root
|
||||
password: Zhuoda1024lab
|
||||
password: 123456
|
||||
initial-size: 2
|
||||
min-idle: 2
|
||||
max-active: 10
|
||||
@@ -27,17 +27,17 @@ spring:
|
||||
|
||||
# redis 连接池配置信息
|
||||
redis:
|
||||
database: 1
|
||||
database: 12
|
||||
host: 127.0.0.1
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 5
|
||||
max-active: 50
|
||||
min-idle: 1
|
||||
max-idle: 3
|
||||
max-wait: 30000ms
|
||||
port: 6379
|
||||
timeout: 10000ms
|
||||
password:
|
||||
timeout: 10s
|
||||
password: 123456
|
||||
|
||||
# 上传文件大小配置
|
||||
servlet:
|
||||
@@ -109,11 +109,6 @@ http:
|
||||
write-timeout: 50000
|
||||
keep-alive: 300000
|
||||
|
||||
# token相关配置
|
||||
token:
|
||||
key: sa-jwt-key
|
||||
expire-day: 7
|
||||
|
||||
# 跨域配置
|
||||
access-control-allow-origin: '*'
|
||||
|
||||
@@ -123,4 +118,23 @@ heart-beat:
|
||||
|
||||
# 热加载配置
|
||||
reload:
|
||||
interval-seconds: 300
|
||||
interval-seconds: 300
|
||||
|
||||
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
|
||||
sa-token:
|
||||
# token 名称(同时也是 cookie 名称)
|
||||
token-name: x-access-token
|
||||
# token 前缀
|
||||
token-prefix:
|
||||
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
|
||||
timeout: 2592000
|
||||
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
|
||||
active-timeout: 28800
|
||||
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
|
||||
is-concurrent: false
|
||||
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
|
||||
is-share: false
|
||||
# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
|
||||
token-style: simple-uuid
|
||||
# 是否输出操作日志
|
||||
is-log: true
|
||||
Reference in New Issue
Block a user