diff --git a/common/common-utils/src/main/java/cn/bunny/common/constant/SQLAutoFillConstant.java b/common/common-utils/src/main/java/cn/bunny/common/constant/SQLAutoFillConstant.java new file mode 100644 index 0000000..67cb03a --- /dev/null +++ b/common/common-utils/src/main/java/cn/bunny/common/constant/SQLAutoFillConstant.java @@ -0,0 +1,11 @@ +package cn.bunny.common.constant; + +/** + * 数据库中自动填充字段 + */ +public class SQLAutoFillConstant { + public static final String SET_CREATE_TIME = "setCreateTime"; + public static final String SET_UPDATE_TIME = "setUpdateTime"; + public static final String SET_CREATE_USER = "setCreateUser"; + public static final String SET_UPDATE_USER = "setUpdateUser"; +} diff --git a/common/common-utils/src/main/java/cn/bunny/common/utils/SnowflakeIdGenerator.java b/common/common-utils/src/main/java/cn/bunny/common/utils/SnowflakeIdGenerator.java index d8f1ce4..fcc0fa2 100644 --- a/common/common-utils/src/main/java/cn/bunny/common/utils/SnowflakeIdGenerator.java +++ b/common/common-utils/src/main/java/cn/bunny/common/utils/SnowflakeIdGenerator.java @@ -9,8 +9,6 @@ import java.util.List; @Component public class SnowflakeIdGenerator { - - // 数据中心id private final long datacenterId; // 数据中心id位数 @@ -47,8 +45,6 @@ public class SnowflakeIdGenerator { private long lastTimestamp = -1L; public SnowflakeIdGenerator(SnowflakeProperties properties) { - - // 数据中心id this.datacenterId = properties.getDatacenterId(); // 数据中心id位数 @@ -76,56 +72,37 @@ public class SnowflakeIdGenerator { // 单次批量生成id的最大数量 this.maxBatchCount = properties.getMaxBatchCount(); - // 校验datacenterId和workerId是否超出最大值 if (datacenterId > maxDatacenterId || datacenterId < 0) { - - throw new IllegalArgumentException(String.format("数据中心Id不能大于%d或小于0", maxDatacenterId)); } if (workerId > maxWorkerId || workerId < 0) { - - throw new IllegalArgumentException(String.format("机器Id不能大于%d或小于0", maxWorkerId)); } } - /** * id生成方法(单个) - * - * @return */ public synchronized long nextId() { - - // 获取当前时间的毫秒数 long timestamp = currentTime(); // 判断时钟是否回拨 if (timestamp < lastTimestamp) { - - throw new RuntimeException(String.format("时钟回拨,回拨毫秒数:%d", lastTimestamp - timestamp)); } // 设置序列号 if (lastTimestamp == timestamp) { - - // 设置序列号递增,如果当前毫秒内序列号已经达到最大值,则直到下一毫秒在重新从0开始计算序列号 sequence = (sequence + 1) & maxSequence; if (sequence == 0) { - - timestamp = tilNextMillis(lastTimestamp); } } else { - - sequence = 0L; } - lastTimestamp = timestamp; // 计算id @@ -138,22 +115,13 @@ public class SnowflakeIdGenerator { /** * id生成方法(批量) - * - * @return */ public synchronized List nextIds(int count) { - - if (count > maxBatchCount || count < 0) { - - throw new IllegalArgumentException(String.format("批量生成id的数量不能大于%d或小于0", maxBatchCount)); } - List ids = new ArrayList<>(count); for (int i = 0; i < count; i++) { - - ids.add(nextId()); } return ids; @@ -164,13 +132,9 @@ public class SnowflakeIdGenerator { * 确保生成的时间戳总是向前移动的,即使在相同的毫秒内请求多个ID时也能保持唯一性。 */ private long tilNextMillis(long lastTimestamp) { - - long timestamp = currentTime(); // 循环等待直至获取到新的毫秒时间戳 while (timestamp <= lastTimestamp) { - - timestamp = currentTime(); } return timestamp; @@ -180,9 +144,6 @@ public class SnowflakeIdGenerator { * 获取当前时间的毫秒数 */ private long currentTime() { - - return System.currentTimeMillis(); } - } \ No newline at end of file diff --git a/common/spring-security/src/main/java/cn/bunny/security/config/WebSecurityConfig.java b/common/spring-security/src/main/java/cn/bunny/security/config/WebSecurityConfig.java index 5615894..5c70d0b 100644 --- a/common/spring-security/src/main/java/cn/bunny/security/config/WebSecurityConfig.java +++ b/common/spring-security/src/main/java/cn/bunny/security/config/WebSecurityConfig.java @@ -2,16 +2,20 @@ package cn.bunny.security.config; import cn.bunny.security.custom.CustomPasswordEncoder; import cn.bunny.security.handelr.*; -import cn.bunny.security.service.UserDetailsService; +import cn.bunny.security.service.MyUserDetailsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.session.SessionRegistry; import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.web.SecurityFilterChain; @@ -25,7 +29,7 @@ public class WebSecurityConfig { @Autowired private RedisTemplate redisTemplate; @Autowired - private UserDetailsService userDetailsService; + private MyUserDetailsService myUserDetailsService; @Autowired private CustomPasswordEncoder customPasswordEncoder; @Autowired @@ -53,7 +57,7 @@ public class WebSecurityConfig { // 后登录的账号会使先登录的账号失效 .sessionManagement(session -> { // 禁用session - // session.sessionCreationPolicy(SessionCreationPolicy.STATELESS); + session.sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 最大登录数为1 session.maximumSessions(1) // 可以获取到所有登录的用户,以及登录状态,设置session状态 @@ -67,8 +71,6 @@ public class WebSecurityConfig { httpSecurity.csrf(AbstractHttpConfigurer::disable); // 跨域访问权限 httpSecurity.cors(withDefaults()); - // 自定义用户认证和密码 - httpSecurity.userDetailsService(userDetailsService).passwordManagement(customPasswordEncoder); // 记住我 httpSecurity.rememberMe(e -> e.rememberMeParameter("rememberBunny").rememberMeCookieName("rememberBunny").key("BunnyKey")); // 自定义过滤器 @@ -79,6 +81,15 @@ public class WebSecurityConfig { return httpSecurity.build(); } + // 自定义用户认证和密码 + @Bean + public AuthenticationManager authenticationManager() { + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setPasswordEncoder(customPasswordEncoder); + provider.setUserDetailsService(myUserDetailsService); + return new ProviderManager(provider); + } + @Bean public SessionRegistry sessionRegistry() { return new SessionRegistryImpl(); diff --git a/common/spring-security/src/main/java/cn/bunny/security/custom/CustomAuthorizationManager.java b/common/spring-security/src/main/java/cn/bunny/security/custom/CustomAuthorizationManager.java new file mode 100644 index 0000000..8c088f2 --- /dev/null +++ b/common/spring-security/src/main/java/cn/bunny/security/custom/CustomAuthorizationManager.java @@ -0,0 +1,19 @@ +package cn.bunny.security.custom; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.access.intercept.RequestAuthorizationContext; +import org.springframework.stereotype.Component; + +import java.util.function.Supplier; + +@Component +@Slf4j +public class CustomAuthorizationManager implements AuthorizationManager { + @Override + public AuthorizationDecision check(Supplier authentication, RequestAuthorizationContext object) { + return null; + } +} diff --git a/common/spring-security/src/main/java/cn/bunny/security/custom/CustomPasswordEncoder.java b/common/spring-security/src/main/java/cn/bunny/security/custom/CustomPasswordEncoder.java index 61fddfd..57864ed 100644 --- a/common/spring-security/src/main/java/cn/bunny/security/custom/CustomPasswordEncoder.java +++ b/common/spring-security/src/main/java/cn/bunny/security/custom/CustomPasswordEncoder.java @@ -12,7 +12,6 @@ import org.springframework.util.DigestUtils; */ @Configuration public class CustomPasswordEncoder implements PasswordEncoder, Customizer> { - @Override public String encode(CharSequence rawPassword) { return DigestUtils.md5DigestAsHex(rawPassword.toString().getBytes()); diff --git a/common/spring-security/src/main/java/cn/bunny/security/service/UserDetailsService.java b/common/spring-security/src/main/java/cn/bunny/security/service/MyUserDetailsService.java similarity index 68% rename from common/spring-security/src/main/java/cn/bunny/security/service/UserDetailsService.java rename to common/spring-security/src/main/java/cn/bunny/security/service/MyUserDetailsService.java index 58bf9be..192d926 100644 --- a/common/spring-security/src/main/java/cn/bunny/security/service/UserDetailsService.java +++ b/common/spring-security/src/main/java/cn/bunny/security/service/MyUserDetailsService.java @@ -2,11 +2,8 @@ package cn.bunny.security.service; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.stereotype.Component; - -@Component -public interface UserDetailsService extends org.springframework.security.core.userdetails.UserDetailsService { +public interface MyUserDetailsService extends org.springframework.security.core.userdetails.UserDetailsService { /** * 根据用户名获取用户对象(获取不到直接抛异常) */ diff --git a/model/src/main/java/cn/bunny/enums/OperationType.java b/model/src/main/java/cn/bunny/enums/OperationType.java new file mode 100644 index 0000000..7e0999e --- /dev/null +++ b/model/src/main/java/cn/bunny/enums/OperationType.java @@ -0,0 +1,8 @@ +package cn.bunny.enums; + +/** + * 数据库操作类型 + */ +public enum OperationType { + UPDATE, INSERT +} diff --git a/pom.xml b/pom.xml index 16c81e2..41d4b4d 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,7 @@ 0.9.1 3.3.3 2.10.1 + 1.9.21 @@ -91,6 +92,18 @@ easyexcel ${easyexcel.version} + + + org.aspectj + aspectjrt + ${aspectj} + + + + org.aspectj + aspectjweaver + ${aspectj} + joda-time joda-time diff --git a/service/pom.xml b/service/pom.xml index e33b71b..c13c891 100644 --- a/service/pom.xml +++ b/service/pom.xml @@ -42,6 +42,20 @@ org.springframework.boot spring-boot-starter-test + + + org.springframework.boot + spring-boot-starter-websocket + + + + org.aspectj + aspectjrt + + + org.aspectj + aspectjweaver + diff --git a/service/src/main/java/cn/bunny/service/ServiceApplication.java b/service/src/main/java/cn/bunny/service/ServiceApplication.java index 67d671c..9ad3a84 100644 --- a/service/src/main/java/cn/bunny/service/ServiceApplication.java +++ b/service/src/main/java/cn/bunny/service/ServiceApplication.java @@ -1,15 +1,22 @@ package cn.bunny.service; +import lombok.extern.slf4j.Slf4j; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.ComponentScan; +import org.springframework.scheduling.annotation.EnableScheduling; -@SpringBootApplication @ComponentScan(basePackages = {"cn.bunny"}) @MapperScan("cn.bunny.service.mapper") +@EnableScheduling// 定时任务 +@EnableCaching// 开启缓存注解 +@SpringBootApplication +@Slf4j public class ServiceApplication { public static void main(String[] args) { + log.info("ServiceApplication启动..."); SpringApplication.run(ServiceApplication.class, args); } } diff --git a/service/src/main/java/cn/bunny/service/annotation/AutoFill.java b/service/src/main/java/cn/bunny/service/annotation/AutoFill.java new file mode 100644 index 0000000..efaf192 --- /dev/null +++ b/service/src/main/java/cn/bunny/service/annotation/AutoFill.java @@ -0,0 +1,15 @@ +package cn.bunny.service.annotation; + +import cn.bunny.enums.OperationType; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface AutoFill { + // 数据库操作类型 + OperationType value(); +} diff --git a/service/src/main/java/cn/bunny/service/aspect/AutoFillAspect.java b/service/src/main/java/cn/bunny/service/aspect/AutoFillAspect.java new file mode 100644 index 0000000..44dc7cb --- /dev/null +++ b/service/src/main/java/cn/bunny/service/aspect/AutoFillAspect.java @@ -0,0 +1,74 @@ +package cn.bunny.service.aspect; + +import cn.bunny.common.constant.SQLAutoFillConstant; +import cn.bunny.common.service.context.BaseContext; +import cn.bunny.enums.OperationType; +import cn.bunny.service.annotation.AutoFill; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; +import java.time.LocalDateTime; + +@Aspect +@Component +@Slf4j +public class AutoFillAspect { + @Pointcut("execution(* cn.bunny.service.*.*(..))") + public void autoFillPointcut() { + } + + /** + * 之前操作 + * + * @param joinPoint 参数 + */ + @Before("autoFillPointcut()") + public void autoFill(JoinPoint joinPoint) { + log.info("开始进行自动填充"); + // 获取当前被拦截数据库操作 + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); + OperationType operationType = autoFill.value(); + // 获取实体对象 + Object[] args = joinPoint.getArgs(); + if (args == null || args.length == 0) { + return; + } + Object entity = args[0]; + // 准备赋值数据 + LocalDateTime localDateTime = LocalDateTime.now(); + Long id = BaseContext.getUserId(); + // 根据当前不同的操作类型,为对应属性来反射赋值 + if (operationType == OperationType.INSERT) { + try { + Method setCreateTime = entity.getClass().getDeclaredMethod(SQLAutoFillConstant.SET_CREATE_TIME, LocalDateTime.class); + Method setCreateUser = entity.getClass().getDeclaredMethod(SQLAutoFillConstant.SET_CREATE_USER, Long.class); + Method setUpdateTime = entity.getClass().getMethod(SQLAutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class); + Method setUpdateUser = entity.getClass().getMethod(SQLAutoFillConstant.SET_UPDATE_USER, Long.class); + + setCreateTime.invoke(entity, localDateTime); + setCreateUser.invoke(entity, id); + setUpdateTime.invoke(entity, localDateTime); + setUpdateUser.invoke(entity, id); + } catch (Exception e) { + e.printStackTrace(); + } + } else if (operationType == OperationType.UPDATE) { + try { + Method setUpdateTime = entity.getClass().getDeclaredMethod(SQLAutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class); + Method setUpdateUser = entity.getClass().getDeclaredMethod(SQLAutoFillConstant.SET_UPDATE_USER, Long.class); + + setUpdateTime.invoke(entity, localDateTime); + setUpdateUser.invoke(entity, id); + } catch (Exception e) { + e.printStackTrace(); + } + } + } +} diff --git a/service/src/main/java/cn/bunny/service/security/MyUserDetailsService.java b/service/src/main/java/cn/bunny/service/security/MyUserDetailsService.java index 782754b..629dc06 100644 --- a/service/src/main/java/cn/bunny/service/security/MyUserDetailsService.java +++ b/service/src/main/java/cn/bunny/service/security/MyUserDetailsService.java @@ -5,9 +5,9 @@ import cn.bunny.common.service.exception.BunnyException; import cn.bunny.entity.system.SysRole; import cn.bunny.entity.system.SysUser; import cn.bunny.security.custom.CustomUser; -import cn.bunny.security.service.UserDetailsService; +import cn.bunny.service.mapper.SysUserMapper; import cn.bunny.service.service.SysRoleService; -import cn.bunny.service.service.SysUserService; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.core.authority.AuthorityUtils; @@ -17,15 +17,15 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import java.util.List; @Configuration -public class MyUserDetailsService implements UserDetailsService { +public class MyUserDetailsService implements cn.bunny.security.service.MyUserDetailsService { @Autowired - private SysUserService sysUserService; + private SysUserMapper sysUserMapper; @Autowired private SysRoleService sysRoleService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - SysUser sysUser = sysUserService.getByUsername(username); + SysUser sysUser = sysUserMapper.selectOne(Wrappers.lambdaQuery().eq(SysUser::getUsername, username)); if (sysUser == null) { throw new UsernameNotFoundException(MessageConstant.USER_DOES_NOT_EXIST); } @@ -35,7 +35,7 @@ public class MyUserDetailsService implements UserDetailsService { } List sysRoleList = sysRoleService.list(); - List roleAuthoritieList = sysRoleList.stream().map(SysRole::getRoleName).toList(); + List roleAuthoritieList = sysRoleList.stream().map(SysRole::getRoleCode).toList(); return new CustomUser(sysUser, AuthorityUtils.createAuthorityList(roleAuthoritieList)); } } diff --git a/service/src/main/java/cn/bunny/service/service/impl/SysUserServiceImpl.java b/service/src/main/java/cn/bunny/service/service/impl/SysUserServiceImpl.java index 9b1b9a2..dd1a0db 100644 --- a/service/src/main/java/cn/bunny/service/service/impl/SysUserServiceImpl.java +++ b/service/src/main/java/cn/bunny/service/service/impl/SysUserServiceImpl.java @@ -4,11 +4,9 @@ import cn.bunny.common.constant.MessageConstant; import cn.bunny.common.service.exception.BunnyException; import cn.bunny.common.utils.SnowflakeIdGenerator; import cn.bunny.entity.system.Login; -import cn.bunny.entity.system.SysRole; import cn.bunny.entity.system.SysUser; import cn.bunny.entity.system.SysUserinfo; import cn.bunny.service.mapper.SysUserMapper; -import cn.bunny.service.service.SysRoleService; import cn.bunny.service.service.SysUserService; import cn.bunny.vo.system.LoginVo; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -17,13 +15,12 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import jakarta.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; import org.springframework.util.DigestUtils; -import java.util.HashMap; -import java.util.List; -import java.util.stream.Collectors; - /** *

* 用户表 服务实现类 @@ -34,12 +31,12 @@ import java.util.stream.Collectors; */ @Service public class SysUserServiceImpl extends ServiceImpl implements SysUserService { - @Autowired - private SysRoleService sysRoleService; @Autowired private RedisTemplate redisTemplate; @Autowired private SnowflakeIdGenerator snowflakeIdGenerator; + @Autowired + private AuthenticationManager authenticationManager; /** * 登录 @@ -73,13 +70,9 @@ public class SysUserServiceImpl extends ServiceImpl impl throw new BunnyException(MessageConstant.PASSWORD_ERROR); } - List roleList = sysRoleService.list().stream().map(SysRole::getRoleCode).collect(Collectors.toList()); - HashMap map = new HashMap<>(); - map.put("sysUser", sysUser); - map.put("roleList", roleList); - - redisTemplate.opsForValue().set(String.valueOf(snowId), map); - + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(vo.getUsername(), vo.getPassword()); + Authentication authenticate = authenticationManager.authenticate(authentication); + redisTemplate.opsForValue().set(String.valueOf(snowId), authenticate); // 添加token return Login.builder().token(String.valueOf(snowId)).build(); } diff --git a/service/src/main/java/cn/bunny/service/task/TemplateTask.java b/service/src/main/java/cn/bunny/service/task/TemplateTask.java new file mode 100644 index 0000000..3016f27 --- /dev/null +++ b/service/src/main/java/cn/bunny/service/task/TemplateTask.java @@ -0,0 +1,12 @@ +package cn.bunny.service.task; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; + +@Slf4j +public class TemplateTask { + @Scheduled(cron = "0/1 * * * * ?") + public void templateTask() { + log.warn("TemplateTask..."); + } +} diff --git a/service/src/main/java/cn/bunny/service/websocket/WebSocketServer.java b/service/src/main/java/cn/bunny/service/websocket/WebSocketServer.java new file mode 100644 index 0000000..d528711 --- /dev/null +++ b/service/src/main/java/cn/bunny/service/websocket/WebSocketServer.java @@ -0,0 +1,71 @@ +package cn.bunny.service.websocket; + +import jakarta.websocket.OnClose; +import jakarta.websocket.OnMessage; +import jakarta.websocket.OnOpen; +import jakarta.websocket.Session; +import jakarta.websocket.server.PathParam; +import jakarta.websocket.server.ServerEndpoint; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * WebSocket服务 + */ +@Component +@ServerEndpoint("/ws/{sid}") +public class WebSocketServer { + + // 存放会话对象 + private static final Map sessionMap = new HashMap(); + + /** + * 连接建立成功调用的方法 + */ + @OnOpen + public void onOpen(Session session, @PathParam("sid") String sid) { + System.out.println("客户端:" + sid + "建立连接"); + sessionMap.put(sid, session); + } + + /** + * 收到客户端消息后调用的方法 + * + * @param message 客户端发送过来的消息 + */ + @OnMessage + public void onMessage(String message, @PathParam("sid") String sid) { + System.out.println("收到来自客户端:" + sid + "的信息:" + message); + } + + /** + * 连接关闭调用的方法 + * + * @param sid 请求id + */ + @OnClose + public void onClose(@PathParam("sid") String sid) { + System.out.println("连接断开:" + sid); + sessionMap.remove(sid); + } + + /** + * 群发 + * + * @param message 消息 + */ + public void sendToAllClient(String message) { + Collection sessions = sessionMap.values(); + for (Session session : sessions) { + try { + // 服务器向客户端发送消息 + session.getBasicRemote().sendText(message); + } catch (Exception e) { + e.printStackTrace(); + } + } + } +}