1、登陆功能todo
This commit is contained in:
parent
a39f94ee27
commit
98950780f5
|
@ -21,6 +21,12 @@
|
||||||
<artifactId>sa-token-spring-boot-starter</artifactId>
|
<artifactId>sa-token-spring-boot-starter</artifactId>
|
||||||
<version>1.43.0</version>
|
<version>1.43.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- Sa-Token 整合 RedisTemplate -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.dev33</groupId>
|
||||||
|
<artifactId>sa-token-redis-template</artifactId>
|
||||||
|
<version>1.43.0</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-devtools</artifactId>
|
<artifactId>spring-boot-devtools</artifactId>
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
//package com.dite.znpt.configuration;
|
|
||||||
//
|
|
||||||
//import cn.dev33.satoken.config.SaTokenConfig;
|
|
||||||
//import cn.dev33.satoken.context.SaHolder;
|
|
||||||
//import cn.dev33.satoken.exception.NotLoginException;
|
|
||||||
//import cn.dev33.satoken.filter.SaServletFilter;
|
|
||||||
//import cn.dev33.satoken.interceptor.SaInterceptor;
|
|
||||||
//import cn.dev33.satoken.stp.StpUtil;
|
|
||||||
//import cn.dev33.satoken.util.SaResult;
|
|
||||||
//import cn.hutool.extra.spring.SpringUtil;
|
|
||||||
//import com.gaea.common.common.constants.Constants;
|
|
||||||
//import com.gaea.common.common.enums.CommonEOS;
|
|
||||||
//import com.gaea.data.base.context.UserContext;
|
|
||||||
//import lombok.extern.slf4j.Slf4j;
|
|
||||||
//import org.springframework.beans.factory.annotation.Value;
|
|
||||||
//import org.springframework.context.annotation.Bean;
|
|
||||||
//import org.springframework.context.annotation.Configuration;
|
|
||||||
//import org.springframework.context.annotation.Primary;
|
|
||||||
//import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
|
||||||
//import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * @description: Sa-Token 权限认证 配置类
|
|
||||||
// */
|
|
||||||
//@Slf4j
|
|
||||||
//@Configuration
|
|
||||||
//public class SaTokenConfigure implements WebMvcConfigurer {
|
|
||||||
//
|
|
||||||
// @Value("${spring.profiles.active}")
|
|
||||||
// private String profile;
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * @author wujinsong
|
|
||||||
// * @date 2021/11/20 9:22 下午
|
|
||||||
// * @description: 注册 Sa-Token 全局过滤器
|
|
||||||
// * @Param []
|
|
||||||
// * @Return cn.dev33.satoken.filter.SaServletFilter
|
|
||||||
// */
|
|
||||||
// @Bean
|
|
||||||
// public SaServletFilter getSaServletFilter() {
|
|
||||||
//
|
|
||||||
// return new SaServletFilter().addInclude("/**").addExclude("/favicon.ico", "/user/login").setAuth(obj -> {
|
|
||||||
// // 校验 Id-Token 身份凭证
|
|
||||||
// if (!Constants.PROFILE_DEV.equals(profile)) {
|
|
||||||
//// StpUtil.checkLogin();
|
|
||||||
// boolean isLogin = SpringUtil.getBean(UserContext.class).checkLogin();
|
|
||||||
// if (!isLogin) {
|
|
||||||
// throw NotLoginException.newInstance(StpUtil.TYPE, NotLoginException.NOT_TOKEN);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }).setError(e -> {
|
|
||||||
// SaHolder.getResponse().setHeader("Content-Type", "application/json; charset=utf-8");
|
|
||||||
// return SaResult.error(CommonEOS.E25000007.getName());
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Bean
|
|
||||||
// @Primary
|
|
||||||
// public SaTokenConfig getSaTokenConfigPrimary() {
|
|
||||||
// SaTokenConfig config = new SaTokenConfig();
|
|
||||||
// config.setTokenName("satoken");
|
|
||||||
// config.setActivityTimeout(6000 * 30);
|
|
||||||
// config.setIsConcurrent(true);
|
|
||||||
// config.setIsShare(true);
|
|
||||||
// config.setTokenStyle("uuid");
|
|
||||||
// config.setIsLog(false);
|
|
||||||
// return config;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // 注册Sa-Token的注解拦截器,打开注解式鉴权功能
|
|
||||||
// @Override
|
|
||||||
// public void addInterceptors(InterceptorRegistry registry) {
|
|
||||||
// // 注册注解拦截器,并排除不需要注解鉴权的接口地址 (与登录拦截器无关)
|
|
||||||
// registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**").excludePathPatterns("/favicon.ico", "/user/login");
|
|
||||||
// }
|
|
||||||
//}
|
|
|
@ -1,32 +1,58 @@
|
||||||
package com.dite.znpt.config;
|
package com.dite.znpt.config;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import cn.dev33.satoken.router.SaRouter;
|
||||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
@Configuration
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
public class WebMvcConfig extends WebMvcConfigurationSupport {
|
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
@Value(value = "${upload.temp-path.image}")
|
|
||||||
private String uploadTempPath;
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
@Value(value = "${upload.perm-path.image}")
|
import java.util.List;
|
||||||
private String uploadPermPath;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MVC 加载Swagger静态资源
|
* @author Bear.G
|
||||||
* @param registry
|
* @date 2025/5/23/周五 11:19
|
||||||
*/
|
* @description
|
||||||
@Override
|
*/
|
||||||
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
|
|
||||||
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
|
@Configuration
|
||||||
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
|
public class WebMvcConfig implements WebMvcConfigurer {
|
||||||
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
|
|
||||||
registry.addResourceHandler("/swagger-ui/**").addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
|
@Value(value = "${upload.temp-path.image}")
|
||||||
registry.addResourceHandler("/static/image/**").addResourceLocations("file:" + uploadPermPath);
|
private String uploadTempPath;
|
||||||
registry.addResourceHandler("/static/image/temp/**").addResourceLocations("file:" + uploadTempPath);
|
|
||||||
registry.addResourceHandler("/upload/**").addResourceLocations("file:D:\\Upload\\Image\\Temp\\");
|
@Value(value = "${upload.perm-path.image}")
|
||||||
super.addResourceHandlers(registry);
|
private String uploadPermPath;
|
||||||
}
|
|
||||||
}
|
@Override
|
||||||
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||||
|
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
|
||||||
|
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
|
||||||
|
registry.addResourceHandler("/swagger-ui/**").addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
|
||||||
|
registry.addResourceHandler("/static/image/**").addResourceLocations("file:" + uploadPermPath);
|
||||||
|
registry.addResourceHandler("/static/image/temp/**").addResourceLocations("file:" + uploadTempPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
|
// 注册 Sa-Token 拦截器,定义详细认证规则
|
||||||
|
// registry.addInterceptor(new SaInterceptor(handler -> {
|
||||||
|
// SaRouter
|
||||||
|
// .match("/**") // 拦截的 path 列表,可以写多个 */
|
||||||
|
// .notMatch(excludePaths())
|
||||||
|
// .check(r -> StpUtil.checkLogin());
|
||||||
|
// })).addPathPatterns("/**");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 动态获取哪些 path 可以忽略鉴权
|
||||||
|
public List<String> excludePaths() {
|
||||||
|
// 此处仅为示例,实际项目你可以写任意代码来查询这些path
|
||||||
|
return Arrays.asList("/login", "/favicon.ico", "/favicon.ico", "/doc.html", "/swagger-ui/**", "/swagger-resources","/webjars/**", "/v3/api-docs/**", "/**/v3/api-docs", "/static/image/**","/static/image/temp/**");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,26 @@ public class Constants {
|
||||||
|
|
||||||
public static final String SERVICE_EXCEPTION_MESSAGE = "服务开小差,请稍后再试!";
|
public static final String SERVICE_EXCEPTION_MESSAGE = "服务开小差,请稍后再试!";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账号密码错误
|
||||||
|
*/
|
||||||
|
public static final String PASSWORD_ERROR_EXCEPTION = "500000";
|
||||||
|
|
||||||
|
public static final String PASSWORD_ERROR_EXCEPTION_MESSAGE = "用户名或者密码错误!";
|
||||||
|
/**
|
||||||
|
* 账号停用
|
||||||
|
*/
|
||||||
|
public static final String USER_DISABLE_EXCEPTION = "500001";
|
||||||
|
|
||||||
|
public static final String USER_DISABLE_EXCEPTION_MESSAGE = "用户已停用!";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认密码
|
||||||
|
*/
|
||||||
|
public static final String DEFAULT_PASSWORD_EXCEPTION = "500002";
|
||||||
|
|
||||||
|
public static final String DEFAULT_PASSWORD_EXCEPTION_MESSAGE = "初始密码,请修改密码后登陆!";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 参数异常
|
* 参数异常
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -41,6 +41,8 @@ public interface Converts {
|
||||||
|
|
||||||
UserResp toUserResp(UserEntity entity);
|
UserResp toUserResp(UserEntity entity);
|
||||||
|
|
||||||
|
UserListResp toUserListResp(UserEntity entity);
|
||||||
|
|
||||||
UserEntity toUserEntity(UserReq req);
|
UserEntity toUserEntity(UserReq req);
|
||||||
|
|
||||||
DeptEntity toDeptEntity(DeptReq req);
|
DeptEntity toDeptEntity(DeptReq req);
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.dite.znpt.domain.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Bear.G
|
||||||
|
* @date 2025/5/22/周四 17:36
|
||||||
|
* @description
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("登录请求实体")
|
||||||
|
public class LoginReq implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = -1782729268877852765L;
|
||||||
|
|
||||||
|
@NotBlank(message = "账号不能为空")
|
||||||
|
@ApiModelProperty("账号")
|
||||||
|
private String account;
|
||||||
|
|
||||||
|
@NotBlank(message = "密码不能为空")
|
||||||
|
@ApiModelProperty("密码,密文传输,采用aes加密,秘钥为账号")
|
||||||
|
private String password;
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.dite.znpt.domain.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Bear.G
|
||||||
|
* @date 2025/5/23/周五 14:54
|
||||||
|
* @description
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel("用户信息")
|
||||||
|
public class UserInfo {
|
||||||
|
|
||||||
|
@ApiModelProperty("用户信息")
|
||||||
|
private UserListResp user;
|
||||||
|
|
||||||
|
@ApiModelProperty("部门信息")
|
||||||
|
private DeptResp dept;
|
||||||
|
|
||||||
|
@ApiModelProperty("岗位信息")
|
||||||
|
private List<PostResp> posts;
|
||||||
|
|
||||||
|
@ApiModelProperty("角色信息")
|
||||||
|
private List<RoleResp> roles;
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package com.dite.znpt.exception;
|
package com.dite.znpt.exception;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.exception.SaTokenException;
|
||||||
import com.dite.znpt.constant.Constants;
|
import com.dite.znpt.constant.Constants;
|
||||||
import com.dite.znpt.domain.Result;
|
import com.dite.znpt.domain.Result;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -17,6 +18,10 @@ import org.springframework.web.bind.annotation.RestController;
|
||||||
@RestController
|
@RestController
|
||||||
public class RestResponseEntityExceptionHandler {
|
public class RestResponseEntityExceptionHandler {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(RestResponseEntityExceptionHandler.class);
|
private static final Logger logger = LoggerFactory.getLogger(RestResponseEntityExceptionHandler.class);
|
||||||
|
@ExceptionHandler(SaTokenException.class)
|
||||||
|
public final Result<?> handlerSaTokenException(SaTokenException e) {
|
||||||
|
return Result.error("5".concat(String.valueOf(e.getCode())), e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
@ExceptionHandler(Exception.class)
|
@ExceptionHandler(Exception.class)
|
||||||
public final Result<?> handler(Exception e) {
|
public final Result<?> handler(Exception e) {
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.dite.znpt.service;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||||
|
import cn.hutool.core.lang.tree.Tree;
|
||||||
|
import com.dite.znpt.domain.Result;
|
||||||
|
import com.dite.znpt.domain.vo.LoginReq;
|
||||||
|
import com.dite.znpt.domain.vo.UserInfo;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Bear.G
|
||||||
|
* @date 2025/5/23/周五 14:31
|
||||||
|
* @description
|
||||||
|
*/
|
||||||
|
public interface LoginService {
|
||||||
|
|
||||||
|
Result<SaTokenInfo> doLogin(LoginReq req);
|
||||||
|
|
||||||
|
List<Tree<String>> getMenuInfo(String userId);
|
||||||
|
|
||||||
|
UserInfo getUserInfo(String userId);
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
package com.dite.znpt.service;
|
package com.dite.znpt.service;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.dite.znpt.domain.entity.PostEntity;
|
||||||
import com.dite.znpt.domain.entity.UserPostEntity;
|
import com.dite.znpt.domain.entity.UserPostEntity;
|
||||||
|
import com.dite.znpt.domain.vo.PostResp;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -11,6 +13,7 @@ import java.util.List;
|
||||||
* @description
|
* @description
|
||||||
*/
|
*/
|
||||||
public interface UserPostService extends IService<UserPostEntity> {
|
public interface UserPostService extends IService<UserPostEntity> {
|
||||||
|
List<PostResp> getPostsByUserId(String userId);
|
||||||
void bindUserPost(String userId, List<String> postIds);
|
void bindUserPost(String userId, List<String> postIds);
|
||||||
void bindPostUser(String postId, List<String> userIds);
|
void bindPostUser(String postId, List<String> userIds);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.dite.znpt.service;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import com.dite.znpt.domain.entity.UserRoleEntity;
|
import com.dite.znpt.domain.entity.UserRoleEntity;
|
||||||
|
import com.dite.znpt.domain.vo.RoleResp;
|
||||||
import com.dite.znpt.domain.vo.UserRoleReq;
|
import com.dite.znpt.domain.vo.UserRoleReq;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -13,6 +14,8 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public interface UserRoleService extends IService<UserRoleEntity> {
|
public interface UserRoleService extends IService<UserRoleEntity> {
|
||||||
|
|
||||||
|
List<RoleResp> getRolesByUserId(String userId);
|
||||||
|
|
||||||
void bindUserRole(UserRoleReq req);
|
void bindUserRole(UserRoleReq req);
|
||||||
|
|
||||||
void bindRoleUser(String roleId, List<String> userIds);
|
void bindRoleUser(String roleId, List<String> userIds);
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
package com.dite.znpt.service.impl;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import cn.hutool.core.lang.tree.Tree;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import com.dite.znpt.constant.Constants;
|
||||||
|
import com.dite.znpt.converts.Converts;
|
||||||
|
import com.dite.znpt.domain.Result;
|
||||||
|
import com.dite.znpt.domain.entity.DeptEntity;
|
||||||
|
import com.dite.znpt.domain.entity.UserEntity;
|
||||||
|
import com.dite.znpt.domain.vo.LoginReq;
|
||||||
|
import com.dite.znpt.domain.vo.UserInfo;
|
||||||
|
import com.dite.znpt.service.*;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Bear.G
|
||||||
|
* @date 2025/5/23/周五 14:31
|
||||||
|
* @description
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class LoginServiceImpl implements LoginService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserRoleService userRoleService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserPostService userPostService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private DeptService deptService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result<SaTokenInfo> doLogin(LoginReq req) {
|
||||||
|
String key = SecureUtil.md5(req.getAccount()).substring(8,24);
|
||||||
|
String password = SecureUtil.aes(key.getBytes()).decryptStr(req.getPassword());
|
||||||
|
UserEntity user = userService.getOne(Wrappers.lambdaQuery(UserEntity.class).eq(UserEntity::getAccount, req.getAccount()).eq(UserEntity::getDelFlag, Constants.DEL_FLAG_0));
|
||||||
|
String pwdCiphertext = SecureUtil.md5(req.getAccount().concat(password).concat(user.getSalt()));
|
||||||
|
if(!pwdCiphertext.equals(user.getPassword())){
|
||||||
|
return Result.error(Constants.PASSWORD_ERROR_EXCEPTION, Constants.PASSWORD_ERROR_EXCEPTION_MESSAGE);
|
||||||
|
}
|
||||||
|
if(!user.getStatus().equals(Constants.STATUS_0)){
|
||||||
|
return Result.error(Constants.USER_DISABLE_EXCEPTION, Constants.USER_DISABLE_EXCEPTION_MESSAGE);
|
||||||
|
}
|
||||||
|
if(user.getIsDefaultPassword()){
|
||||||
|
return Result.error(Constants.DEFAULT_PASSWORD_EXCEPTION, Constants.DEFAULT_PASSWORD_EXCEPTION_MESSAGE);
|
||||||
|
}
|
||||||
|
StpUtil.login(user.getUserId());
|
||||||
|
saveUserSession(user);
|
||||||
|
return Result.ok(StpUtil.getTokenInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Tree<String>> getMenuInfo(String userId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserInfo getUserInfo(String userId) {
|
||||||
|
UserInfo userInfo = new UserInfo();
|
||||||
|
UserEntity user = userService.getOne(Wrappers.lambdaQuery(UserEntity.class).eq(UserEntity::getAccount, userId).eq(UserEntity::getDelFlag, Constants.DEL_FLAG_0));
|
||||||
|
userInfo.setUser(Converts.INSTANCE.toUserListResp(user));
|
||||||
|
DeptEntity dept = deptService.getOne(Wrappers.lambdaQuery(DeptEntity.class).eq(StrUtil.isNotBlank(user.getDeptId()), DeptEntity::getDeptId, user.getDeptId()).eq(DeptEntity::getDelFlag, Constants.DEL_FLAG_0));
|
||||||
|
userInfo.setDept(Converts.INSTANCE.toDeptResp(dept));
|
||||||
|
userInfo.setRoles(userRoleService.getRolesByUserId(userId));
|
||||||
|
userInfo.setPosts(userPostService.getPostsByUserId(userId));
|
||||||
|
return userInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveUserSession(UserEntity user){
|
||||||
|
StpUtil.getSession().set(user.getUserId(), user);
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,9 +5,11 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.dite.znpt.constant.Constants;
|
import com.dite.znpt.constant.Constants;
|
||||||
import com.dite.znpt.constant.Message;
|
import com.dite.znpt.constant.Message;
|
||||||
|
import com.dite.znpt.converts.Converts;
|
||||||
import com.dite.znpt.domain.entity.PostEntity;
|
import com.dite.znpt.domain.entity.PostEntity;
|
||||||
import com.dite.znpt.domain.entity.UserEntity;
|
import com.dite.znpt.domain.entity.UserEntity;
|
||||||
import com.dite.znpt.domain.entity.UserPostEntity;
|
import com.dite.znpt.domain.entity.UserPostEntity;
|
||||||
|
import com.dite.znpt.domain.vo.PostResp;
|
||||||
import com.dite.znpt.exception.ServiceException;
|
import com.dite.znpt.exception.ServiceException;
|
||||||
import com.dite.znpt.mapper.UserPostMapper;
|
import com.dite.znpt.mapper.UserPostMapper;
|
||||||
import com.dite.znpt.service.PostService;
|
import com.dite.znpt.service.PostService;
|
||||||
|
@ -33,6 +35,13 @@ public class UserPostServiceImpl extends ServiceImpl<UserPostMapper, UserPostEnt
|
||||||
@Resource
|
@Resource
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PostResp> getPostsByUserId(String userId) {
|
||||||
|
List<String> postIds = this.list(Wrappers.lambdaQuery(UserPostEntity.class).eq(UserPostEntity::getUserId, userId)).stream().map(UserPostEntity::getPostId).toList();
|
||||||
|
List<PostEntity> posts= postService.listByIds(postIds).stream().filter(post -> Constants.STATUS_0.equals(post.getStatus())).toList();
|
||||||
|
return Converts.INSTANCE.toPostResp(posts);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void bindUserPost(String userId, List<String> postIds) {
|
public void bindUserPost(String userId, List<String> postIds) {
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
package com.dite.znpt.service.impl;
|
package com.dite.znpt.service.impl;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import com.dite.znpt.constant.Constants;
|
import com.dite.znpt.constant.Constants;
|
||||||
import com.dite.znpt.constant.Message;
|
import com.dite.znpt.constant.Message;
|
||||||
|
import com.dite.znpt.converts.Converts;
|
||||||
import com.dite.znpt.domain.entity.RoleEntity;
|
import com.dite.znpt.domain.entity.RoleEntity;
|
||||||
import com.dite.znpt.domain.entity.UserEntity;
|
import com.dite.znpt.domain.entity.UserEntity;
|
||||||
import com.dite.znpt.domain.entity.UserRoleEntity;
|
import com.dite.znpt.domain.entity.UserRoleEntity;
|
||||||
|
import com.dite.znpt.domain.vo.RoleResp;
|
||||||
import com.dite.znpt.domain.vo.UserRoleReq;
|
import com.dite.znpt.domain.vo.UserRoleReq;
|
||||||
import com.dite.znpt.exception.ServiceException;
|
import com.dite.znpt.exception.ServiceException;
|
||||||
import com.dite.znpt.mapper.UserRoleMapper;
|
import com.dite.znpt.mapper.UserRoleMapper;
|
||||||
|
@ -34,6 +37,13 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRoleEnt
|
||||||
@Resource
|
@Resource
|
||||||
private RoleService roleService;
|
private RoleService roleService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<RoleResp> getRolesByUserId(String userId) {
|
||||||
|
List<String> roleIds = this.list(Wrappers.lambdaQuery(UserRoleEntity.class).eq(UserRoleEntity::getUserId, userId)).stream().map(UserRoleEntity::getRoleId).toList();
|
||||||
|
List<RoleEntity> roles = roleService.listByIds(roleIds).stream().filter(role -> Constants.DEL_FLAG_0.equals(role.getDelFlag()) && Constants.STATUS_0.equals(role.getStatus())).toList();
|
||||||
|
return Converts.INSTANCE.toRoleResp(roles);
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
@Override
|
@Override
|
||||||
public void bindUserRole(UserRoleReq req) {
|
public void bindUserRole(UserRoleReq req) {
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
package com.dite.znpt.util;
|
package com.dite.znpt.util;
|
||||||
|
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -10,15 +7,6 @@ import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class PasswordUtil {
|
public class PasswordUtil {
|
||||||
private static final PasswordEncoder encoder = new BCryptPasswordEncoder();
|
|
||||||
|
|
||||||
public static String encrypt(String rawPassword) {
|
|
||||||
return encoder.encode(rawPassword);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean matches(String rawPassword, String encodedPassword) {
|
|
||||||
return encoder.matches(rawPassword, encodedPassword);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 字符集定义
|
// 字符集定义
|
||||||
private static final String UPPER_CASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
private static final String UPPER_CASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
|
|
@ -0,0 +1,459 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>AES加密解密工具</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" rel="stylesheet">
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
|
||||||
|
<script>
|
||||||
|
tailwind.config = {
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
primary: '#3b82f6',
|
||||||
|
secondary: '#10b981',
|
||||||
|
accent: '#6366f1',
|
||||||
|
dark: '#1e293b',
|
||||||
|
light: '#f8fafc'
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
inter: ['Inter', 'system-ui', 'sans-serif'],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style type="text/tailwindcss">
|
||||||
|
@layer utilities {
|
||||||
|
.content-auto {
|
||||||
|
content-visibility: auto;
|
||||||
|
}
|
||||||
|
.text-shadow {
|
||||||
|
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.card-shadow {
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
.transition-custom {
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="font-inter bg-gradient-to-br from-light to-blue-50 min-h-screen">
|
||||||
|
<div class="container mx-auto px-4 py-8 max-w-5xl">
|
||||||
|
<!-- 标题区域 -->
|
||||||
|
<header class="mb-12 text-center">
|
||||||
|
<h1 class="text-[clamp(2rem,5vw,3.5rem)] font-bold text-dark mb-3 text-shadow">
|
||||||
|
<i class="fa-solid fa-lock text-primary mr-3"></i>AES加密解密工具
|
||||||
|
</h1>
|
||||||
|
<p class="text-gray-600 text-lg max-w-2xl mx-auto">
|
||||||
|
使用先进的AES加密算法保护您的数据安全,支持多种加密模式和密钥长度
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- 主内容区 -->
|
||||||
|
<main class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||||
|
<!-- 加密区域 -->
|
||||||
|
<section class="bg-white rounded-2xl p-6 card-shadow transition-custom hover:shadow-xl">
|
||||||
|
<h2 class="text-2xl font-bold text-dark mb-6 flex items-center">
|
||||||
|
<i class="fa-solid fa-shield-lock text-primary mr-2"></i>加密
|
||||||
|
</h2>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label for="plaintext" class="block text-sm font-medium text-gray-700 mb-1">明文</label>
|
||||||
|
<textarea id="plaintext" class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary/50 focus:border-primary transition-custom resize-none" rows="4" placeholder="请输入要加密的文本..."></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="encrypt-key" class="block text-sm font-medium text-gray-700 mb-1">密钥</label>
|
||||||
|
<div class="relative">
|
||||||
|
<input type="text" id="encrypt-key" class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary/50 focus:border-primary transition-custom" placeholder="请输入16/24/32字节密钥..." value="1234567890123456">
|
||||||
|
<button type="button" id="generate-key" class="absolute right-2 top-1/2 -translate-y-1/2 text-primary hover:text-primary/80 transition-custom">
|
||||||
|
<i class="fa-solid fa-refresh"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-1">加密选项</label>
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label for="encrypt-mode" class="block text-xs text-gray-500 mb-1">加密模式</label>
|
||||||
|
<select id="encrypt-mode" class="w-full px-3 py-2 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary/50 focus:border-primary transition-custom">
|
||||||
|
<option value="ECB">ECB</option>
|
||||||
|
<option value="CBC">CBC</option>
|
||||||
|
<option value="CFB">CFB</option>
|
||||||
|
<option value="OFB">OFB</option>
|
||||||
|
<option value="CTR">CTR</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="encrypt-padding" class="block text-xs text-gray-500 mb-1">填充方式</label>
|
||||||
|
<select id="encrypt-padding" class="w-full px-3 py-2 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary/50 focus:border-primary transition-custom">
|
||||||
|
<option value="Pkcs7">PKCS#7</option>
|
||||||
|
<option value="Iso97971">ISO 9797-1</option>
|
||||||
|
<option value="AnsiX923">ANSI X.923</option>
|
||||||
|
<option value="Iso10126">ISO 10126</option>
|
||||||
|
<option value="ZeroPadding">Zero Padding</option>
|
||||||
|
<option value="NoPadding">No Padding</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="iv-container" class="hidden">
|
||||||
|
<label for="encrypt-iv" class="block text-sm font-medium text-gray-700 mb-1">初始化向量(IV)</label>
|
||||||
|
<input type="text" id="encrypt-iv" class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary/50 focus:border-primary transition-custom" placeholder="请输入16字节初始化向量...">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button id="encrypt-btn" class="w-full bg-primary hover:bg-primary/90 text-white font-medium py-3 px-4 rounded-lg transition-custom transform hover:scale-[1.02] flex items-center justify-center">
|
||||||
|
<i class="fa-solid fa-lock mr-2"></i>加密
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- 解密区域 -->
|
||||||
|
<section class="bg-white rounded-2xl p-6 card-shadow transition-custom hover:shadow-xl">
|
||||||
|
<h2 class="text-2xl font-bold text-dark mb-6 flex items-center">
|
||||||
|
<i class="fa-solid fa-unlock text-secondary mr-2"></i>解密
|
||||||
|
</h2>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label for="ciphertext" class="block text-sm font-medium text-gray-700 mb-1">密文</label>
|
||||||
|
<textarea id="ciphertext" class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-secondary/50 focus:border-secondary transition-custom resize-none" rows="4" placeholder="请输入要解密的文本..."></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="decrypt-key" class="block text-sm font-medium text-gray-700 mb-1">密钥</label>
|
||||||
|
<input type="text" id="decrypt-key" class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-secondary/50 focus:border-secondary transition-custom" placeholder="请输入16/24/32字节密钥..." value="1234567890123456">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-1">解密选项</label>
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label for="decrypt-mode" class="block text-xs text-gray-500 mb-1">解密模式</label>
|
||||||
|
<select id="decrypt-mode" class="w-full px-3 py-2 rounded-lg border border-gray-300 focus:ring-2 focus:ring-secondary/50 focus:border-secondary transition-custom">
|
||||||
|
<option value="ECB">ECB</option>
|
||||||
|
<option value="CBC">CBC</option>
|
||||||
|
<option value="CFB">CFB</option>
|
||||||
|
<option value="OFB">OFB</option>
|
||||||
|
<option value="CTR">CTR</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="decrypt-padding" class="block text-xs text-gray-500 mb-1">填充方式</label>
|
||||||
|
<select id="decrypt-padding" class="w-full px-3 py-2 rounded-lg border border-gray-300 focus:ring-2 focus:ring-secondary/50 focus:border-secondary transition-custom">
|
||||||
|
<option value="Pkcs7">PKCS#7</option>
|
||||||
|
<option value="Iso97971">ISO 9797-1</option>
|
||||||
|
<option value="AnsiX923">ANSI X.923</option>
|
||||||
|
<option value="Iso10126">ISO 10126</option>
|
||||||
|
<option value="ZeroPadding">Zero Padding</option>
|
||||||
|
<option value="NoPadding">No Padding</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="decrypt-iv-container" class="hidden">
|
||||||
|
<label for="decrypt-iv" class="block text-sm font-medium text-gray-700 mb-1">初始化向量(IV)</label>
|
||||||
|
<input type="text" id="decrypt-iv" class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-secondary/50 focus:border-secondary transition-custom" placeholder="请输入16字节初始化向量...">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button id="decrypt-btn" class="w-full bg-secondary hover:bg-secondary/90 text-white font-medium py-3 px-4 rounded-lg transition-custom transform hover:scale-[1.02] flex items-center justify-center">
|
||||||
|
<i class="fa-solid fa-unlock mr-2"></i>解密
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- 结果展示区 -->
|
||||||
|
<section class="mt-8 bg-white rounded-2xl p-6 card-shadow">
|
||||||
|
<h2 class="text-2xl font-bold text-dark mb-4 flex items-center">
|
||||||
|
<i class="fa-solid fa-file-text text-accent mr-2"></i>结果
|
||||||
|
</h2>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-1">加密结果</label>
|
||||||
|
<div class="relative">
|
||||||
|
<textarea id="encrypt-result" class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-accent/50 focus:border-accent transition-custom resize-none" rows="4" readonly placeholder="加密结果将显示在这里..."></textarea>
|
||||||
|
<button id="copy-encrypt" class="absolute right-2 top-2 text-gray-500 hover:text-accent transition-custom">
|
||||||
|
<i class="fa-solid fa-copy"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-1">解密结果</label>
|
||||||
|
<div class="relative">
|
||||||
|
<textarea id="decrypt-result" class="w-full px-4 py-3 rounded-lg border border-gray-300 focus:ring-2 focus:ring-accent/50 focus:border-accent transition-custom resize-none" rows="4" readonly placeholder="解密结果将显示在这里..."></textarea>
|
||||||
|
<button id="copy-decrypt" class="absolute right-2 top-2 text-gray-500 hover:text-accent transition-custom">
|
||||||
|
<i class="fa-solid fa-copy"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- 信息提示区 -->
|
||||||
|
<div id="notification" class="fixed bottom-4 right-4 bg-dark text-white px-6 py-3 rounded-lg shadow-lg transform translate-y-20 opacity-0 transition-all duration-300 flex items-center">
|
||||||
|
<i id="notification-icon" class="fa-solid fa-info-circle mr-2"></i>
|
||||||
|
<span id="notification-message">操作成功</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 加密函数
|
||||||
|
function encrypt() {
|
||||||
|
const plaintext = document.getElementById('plaintext').value.trim();
|
||||||
|
const key = document.getElementById('encrypt-key').value.trim();
|
||||||
|
const mode = document.getElementById('encrypt-mode').value;
|
||||||
|
const padding = document.getElementById('encrypt-padding').value;
|
||||||
|
const iv = document.getElementById('encrypt-iv').value.trim();
|
||||||
|
|
||||||
|
if (!plaintext) {
|
||||||
|
showNotification('请输入明文', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key) {
|
||||||
|
showNotification('请输入密钥', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取加密模式
|
||||||
|
const modeObj = getModeObject(mode);
|
||||||
|
|
||||||
|
// 获取填充方式
|
||||||
|
const paddingObj = getPaddingObject(padding);
|
||||||
|
|
||||||
|
// 准备加密参数
|
||||||
|
const keyUtf8 = CryptoJS.enc.Utf8.parse(key);
|
||||||
|
let encrypted;
|
||||||
|
|
||||||
|
// 根据模式是否需要IV
|
||||||
|
if (mode === 'ECB') {
|
||||||
|
encrypted = CryptoJS.AES.encrypt(plaintext, keyUtf8, {
|
||||||
|
mode: modeObj,
|
||||||
|
padding: paddingObj
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (!iv) {
|
||||||
|
showNotification('使用该模式需要输入初始化向量(IV)', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ivUtf8 = CryptoJS.enc.Utf8.parse(iv);
|
||||||
|
encrypted = CryptoJS.AES.encrypt(plaintext, keyUtf8, {
|
||||||
|
iv: ivUtf8,
|
||||||
|
mode: modeObj,
|
||||||
|
padding: paddingObj
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示加密结果
|
||||||
|
const encryptedResult = encrypted.toString();
|
||||||
|
document.getElementById('encrypt-result').value = encryptedResult;
|
||||||
|
document.getElementById('ciphertext').value = encryptedResult;
|
||||||
|
|
||||||
|
showNotification('加密成功', 'success');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加密错误:', error);
|
||||||
|
showNotification('加密失败: ' + error.message, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解密函数
|
||||||
|
function decrypt() {
|
||||||
|
const ciphertext = document.getElementById('ciphertext').value.trim();
|
||||||
|
const key = document.getElementById('decrypt-key').value.trim();
|
||||||
|
const mode = document.getElementById('decrypt-mode').value;
|
||||||
|
const padding = document.getElementById('decrypt-padding').value;
|
||||||
|
const iv = document.getElementById('decrypt-iv').value.trim();
|
||||||
|
|
||||||
|
if (!ciphertext) {
|
||||||
|
showNotification('请输入密文', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key) {
|
||||||
|
showNotification('请输入密钥', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取解密模式
|
||||||
|
const modeObj = getModeObject(mode);
|
||||||
|
|
||||||
|
// 获取填充方式
|
||||||
|
const paddingObj = getPaddingObject(padding);
|
||||||
|
|
||||||
|
// 准备解密参数
|
||||||
|
const keyUtf8 = CryptoJS.enc.Utf8.parse(key);
|
||||||
|
|
||||||
|
// 根据模式是否需要IV
|
||||||
|
let decrypted;
|
||||||
|
if (mode === 'ECB') {
|
||||||
|
decrypted = CryptoJS.AES.decrypt(ciphertext, keyUtf8, {
|
||||||
|
mode: modeObj,
|
||||||
|
padding: paddingObj
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (!iv) {
|
||||||
|
showNotification('使用该模式需要输入初始化向量(IV)', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ivUtf8 = CryptoJS.enc.Utf8.parse(iv);
|
||||||
|
decrypted = CryptoJS.AES.decrypt(ciphertext, keyUtf8, {
|
||||||
|
iv: ivUtf8,
|
||||||
|
mode: modeObj,
|
||||||
|
padding: paddingObj
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示解密结果
|
||||||
|
const decryptedResult = decrypted.toString(CryptoJS.enc.Utf8);
|
||||||
|
document.getElementById('decrypt-result').value = decryptedResult;
|
||||||
|
|
||||||
|
showNotification('解密成功', 'success');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解密错误:', error);
|
||||||
|
showNotification('解密失败: ' + error.message, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据名称获取加密/解密模式对象
|
||||||
|
function getModeObject(modeName) {
|
||||||
|
switch (modeName) {
|
||||||
|
case 'ECB': return CryptoJS.mode.ECB;
|
||||||
|
case 'CBC': return CryptoJS.mode.CBC;
|
||||||
|
case 'CFB': return CryptoJS.mode.CFB;
|
||||||
|
case 'OFB': return CryptoJS.mode.OFB;
|
||||||
|
case 'CTR': return CryptoJS.mode.CTR;
|
||||||
|
default: return CryptoJS.mode.ECB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据名称获取填充方式对象
|
||||||
|
function getPaddingObject(paddingName) {
|
||||||
|
switch (paddingName) {
|
||||||
|
case 'Pkcs7': return CryptoJS.pad.Pkcs7;
|
||||||
|
case 'Iso97971': return CryptoJS.pad.Iso97971;
|
||||||
|
case 'AnsiX923': return CryptoJS.pad.AnsiX923;
|
||||||
|
case 'Iso10126': return CryptoJS.pad.Iso10126;
|
||||||
|
case 'ZeroPadding': return CryptoJS.pad.ZeroPadding;
|
||||||
|
case 'NoPadding': return CryptoJS.pad.NoPadding;
|
||||||
|
default: return CryptoJS.pad.Pkcs7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成随机密钥
|
||||||
|
function generateRandomKey(length = 16) {
|
||||||
|
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
let key = "";
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
key += charset.charAt(Math.floor(Math.random() * charset.length));
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示通知
|
||||||
|
function showNotification(message, type = 'info') {
|
||||||
|
const notification = document.getElementById('notification');
|
||||||
|
const notificationIcon = document.getElementById('notification-icon');
|
||||||
|
const notificationMessage = document.getElementById('notification-message');
|
||||||
|
|
||||||
|
// 设置通知类型
|
||||||
|
notification.className = 'fixed bottom-4 right-4 px-6 py-3 rounded-lg shadow-lg transform translate-y-20 opacity-0 transition-all duration-300 flex items-center';
|
||||||
|
|
||||||
|
if (type === 'success') {
|
||||||
|
notification.classList.add('bg-green-500', 'text-white');
|
||||||
|
notificationIcon.className = 'fa-solid fa-check-circle mr-2';
|
||||||
|
} else if (type === 'error') {
|
||||||
|
notification.classList.add('bg-red-500', 'text-white');
|
||||||
|
notificationIcon.className = 'fa-solid fa-exclamation-circle mr-2';
|
||||||
|
} else {
|
||||||
|
notification.classList.add('bg-dark', 'text-white');
|
||||||
|
notificationIcon.className = 'fa-solid fa-info-circle mr-2';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置通知消息
|
||||||
|
notificationMessage.textContent = message;
|
||||||
|
|
||||||
|
// 显示通知
|
||||||
|
setTimeout(() => {
|
||||||
|
notification.classList.remove('translate-y-20', 'opacity-0');
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
// 3秒后隐藏通知
|
||||||
|
setTimeout(() => {
|
||||||
|
notification.classList.add('translate-y-20', 'opacity-0');
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制到剪贴板
|
||||||
|
function copyToClipboard(elementId) {
|
||||||
|
const element = document.getElementById(elementId);
|
||||||
|
element.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
showNotification('已复制到剪贴板', 'success');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模式变更事件处理
|
||||||
|
function handleModeChange() {
|
||||||
|
const encryptMode = document.getElementById('encrypt-mode').value;
|
||||||
|
const decryptMode = document.getElementById('decrypt-mode').value;
|
||||||
|
|
||||||
|
// 显示或隐藏加密IV输入框
|
||||||
|
const encryptIvContainer = document.getElementById('iv-container');
|
||||||
|
if (encryptMode === 'ECB') {
|
||||||
|
encryptIvContainer.classList.add('hidden');
|
||||||
|
} else {
|
||||||
|
encryptIvContainer.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示或隐藏解密IV输入框
|
||||||
|
const decryptIvContainer = document.getElementById('decrypt-iv-container');
|
||||||
|
if (decryptMode === 'ECB') {
|
||||||
|
decryptIvContainer.classList.add('hidden');
|
||||||
|
} else {
|
||||||
|
decryptIvContainer.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成随机密钥
|
||||||
|
document.getElementById('generate-key').addEventListener('click', () => {
|
||||||
|
document.getElementById('encrypt-key').value = generateRandomKey(16);
|
||||||
|
document.getElementById('decrypt-key').value = document.getElementById('encrypt-key').value;
|
||||||
|
showNotification('已生成随机密钥', 'success');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 加密按钮点击事件
|
||||||
|
document.getElementById('encrypt-btn').addEventListener('click', encrypt);
|
||||||
|
|
||||||
|
// 解密按钮点击事件
|
||||||
|
document.getElementById('decrypt-btn').addEventListener('click', decrypt);
|
||||||
|
|
||||||
|
// 复制加密结果
|
||||||
|
document.getElementById('copy-encrypt').addEventListener('click', () => copyToClipboard('encrypt-result'));
|
||||||
|
|
||||||
|
// 复制解密结果
|
||||||
|
document.getElementById('copy-decrypt').addEventListener('click', () => copyToClipboard('decrypt-result'));
|
||||||
|
|
||||||
|
// 模式变更事件
|
||||||
|
document.getElementById('encrypt-mode').addEventListener('change', handleModeChange);
|
||||||
|
document.getElementById('decrypt-mode').addEventListener('change', handleModeChange);
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
handleModeChange();
|
||||||
|
|
||||||
|
// 添加示例数据
|
||||||
|
document.getElementById('plaintext').value = '这是一个使用AES加密的示例文本,您可以替换为自己的内容。';
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
|
@ -1,9 +1,24 @@
|
||||||
package com.dite.znpt.web.controller;
|
package com.dite.znpt.web.controller;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.SaTokenInfo;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import cn.hutool.crypto.SecureUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import com.dite.znpt.constant.Constants;
|
||||||
|
import com.dite.znpt.domain.Result;
|
||||||
|
import com.dite.znpt.domain.entity.UserEntity;
|
||||||
|
import com.dite.znpt.domain.vo.LoginReq;
|
||||||
|
import com.dite.znpt.service.LoginService;
|
||||||
|
import com.dite.znpt.service.UserService;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Bear.G
|
* @author Bear.G
|
||||||
* @date 2025/5/19/周一 14:32
|
* @date 2025/5/19/周一 14:32
|
||||||
|
@ -12,4 +27,13 @@ import org.springframework.web.bind.annotation.RestController;
|
||||||
@Api(tags = "登录")
|
@Api(tags = "登录")
|
||||||
@RestController
|
@RestController
|
||||||
public class LoginController {
|
public class LoginController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private LoginService loginService;
|
||||||
|
|
||||||
|
@ApiOperation(value = "登陆",httpMethod = "POST")
|
||||||
|
@PostMapping("/login")
|
||||||
|
public Result<SaTokenInfo> login(@Validated @RequestBody LoginReq req) {
|
||||||
|
return loginService.doLogin(req);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,23 @@ spring:
|
||||||
# domain:
|
# domain:
|
||||||
# prefix: /minio/
|
# prefix: /minio/
|
||||||
|
|
||||||
|
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
|
||||||
|
sa-token:
|
||||||
|
# token 名称(同时也是 cookie 名称)
|
||||||
|
token-name: Authorization
|
||||||
|
# token 有效期(单位:秒) 默认30天,-1 代表永久有效
|
||||||
|
timeout: 2592000
|
||||||
|
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
|
||||||
|
active-timeout: -1
|
||||||
|
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
|
||||||
|
is-concurrent: true
|
||||||
|
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)
|
||||||
|
is-share: false
|
||||||
|
# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
|
||||||
|
token-style: uuid
|
||||||
|
# 是否输出操作日志
|
||||||
|
is-log: true
|
||||||
|
|
||||||
sip-config:
|
sip-config:
|
||||||
name: 信令服务
|
name: 信令服务
|
||||||
ip: 127.0.0.1
|
ip: 127.0.0.1
|
||||||
|
|
Loading…
Reference in New Issue