diff --git a/.gitignore b/.gitignore index a593848..c4e9921 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ logs/ .springBeans .sts4-cache +application-prod.yml ### IntelliJ IDEA ### .idea diff --git a/ReadMe.md b/ReadMe.md index 6cb0a49..50187b2 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,3 +1,11 @@ +# 感谢 + +项目由[小铭](https://github.com/xiaoxian521)开源权限模板[Pure-admin](https://pure-admin.github.io/vue-pure-admin/) + +Pure-admin文档:https://pure-admin.github.io/pure-admin-doc + +项目整体都由此模板开发 + # 项目预览 不知道为什么,图床用的使自己的,Gitee就是不显示其它GitHub和Gitea都能显示就Gitee显示不出来,如果想用Gitee就把ReadMe文件下载下来也行;或者把项目clone下来看也可以 diff --git a/common/common-generator/src/main/java/cn/bunny/common/generator/generator/WebGeneratorCode.java b/common/common-generator/src/main/java/cn/bunny/common/generator/generator/WebGeneratorCode.java index b2ac0c0..2f8d550 100644 --- a/common/common-generator/src/main/java/cn/bunny/common/generator/generator/WebGeneratorCode.java +++ b/common/common-generator/src/main/java/cn/bunny/common/generator/generator/WebGeneratorCode.java @@ -3,11 +3,11 @@ package cn.bunny.common.generator.generator; import cn.bunny.common.generator.entity.BaseField; import cn.bunny.common.generator.entity.BaseResultMap; import cn.bunny.common.generator.utils.GeneratorCodeUtils; -import cn.bunny.dao.dto.financial.category.CategoryAddDto; -import cn.bunny.dao.dto.financial.category.CategoryDto; -import cn.bunny.dao.dto.financial.category.CategoryUpdateDto; -import cn.bunny.dao.entity.financial.Category; -import cn.bunny.dao.vo.financial.CategoryVo; +import cn.bunny.dao.dto.system.user.AdminUserAddDto; +import cn.bunny.dao.dto.system.user.AdminUserDto; +import cn.bunny.dao.dto.system.user.AdminUserUpdateDto; +import cn.bunny.dao.entity.system.AdminUser; +import cn.bunny.dao.vo.system.user.AdminUserVo; import com.baomidou.mybatisplus.annotation.TableName; import com.google.common.base.CaseFormat; import io.swagger.v3.oas.annotations.media.Schema; @@ -52,11 +52,11 @@ public class WebGeneratorCode { public static String resourceMapperPath = "D:\\MyFolder\\financial\\financial-server\\service\\src\\main\\resources\\mapper\\financial\\"; public static void main(String[] args) throws Exception { - Class originalClass = Category.class; - Class dtoClass = CategoryDto.class; - Class addDtoClass = CategoryAddDto.class; - Class updateDtoClass = CategoryUpdateDto.class; - Class voClass = CategoryVo.class; + Class originalClass = AdminUser.class; + Class dtoClass = AdminUserDto.class; + Class addDtoClass = AdminUserAddDto.class; + Class updateDtoClass = AdminUserUpdateDto.class; + Class voClass = AdminUserVo.class; // 设置velocity资源加载器 Properties prop = new Properties(); diff --git a/service/Dockerfile b/service/Dockerfile index 18d83a4..0039cda 100644 --- a/service/Dockerfile +++ b/service/Dockerfile @@ -27,7 +27,7 @@ ENTRYPOINT ["java","-jar","/home/server/app.jar"] #暴露 8000 端口 EXPOSE 8000 -# 开发环境 +# 生产环境 # mvn clean package -Pprod -DskipTests # 测试环境 diff --git a/service/src/main/java/cn/bunny/services/security/config/WebSecurityConfig.java b/service/src/main/java/cn/bunny/services/security/config/WebSecurityConfig.java index 550dcfd..ec247a8 100644 --- a/service/src/main/java/cn/bunny/services/security/config/WebSecurityConfig.java +++ b/service/src/main/java/cn/bunny/services/security/config/WebSecurityConfig.java @@ -1,8 +1,6 @@ package cn.bunny.services.security.config; import cn.bunny.services.security.custom.CustomPasswordEncoder; -import cn.bunny.services.security.filter.NoTokenAuthenticationFilter; -import cn.bunny.services.security.filter.TokenAuthenticationFilter; import cn.bunny.services.security.filter.TokenLoginFilterService; import cn.bunny.services.security.handelr.SecurityAccessDeniedHandler; import cn.bunny.services.security.handelr.SecurityAuthenticationEntryPoint; @@ -16,7 +14,6 @@ import org.springframework.security.config.annotation.authentication.configurati 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.configuration.WebSecurityCustomizer; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @@ -43,6 +40,12 @@ public class WebSecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { + String[] annotations = { + "/", "/ws/**", + "/*/*/noAuth/**", "/*/noAuth/**", "/noAuth/**", + "/media.ico", "/favicon.ico", "*.html", "/webjars/**", "/v3/api-docs/**", "swagger-ui/**", + "/error", "/*/i18n/getI18n", + }; httpSecurity // 前端段分离不需要---禁用明文验证 .httpBasic(AbstractHttpConfigurer::disable) @@ -56,9 +59,11 @@ public class WebSecurityConfig { .cors(AbstractHttpConfigurer::disable) // 前后端分离不需要---因为是无状态的 .sessionManagement(AbstractHttpConfigurer::disable) - // 前后端分离不需要---记住我,e -> e.rememberMeParameter("rememberBunny").rememberMeCookieName("rememberBunny").key("BunnyKey") + // 前后端分离不需要---记住我 .rememberMe(AbstractHttpConfigurer::disable) - .authorizeHttpRequests(authorize -> authorize.anyRequest().access(customAuthorizationManagerService)) + .authorizeHttpRequests(authorize -> authorize.requestMatchers(annotations).permitAll() + .requestMatchers(RegexRequestMatcher.regexMatcher(".*\\.(css|js)$")).permitAll() + .anyRequest().access(customAuthorizationManagerService)) .exceptionHandling(exception -> { // 请求未授权接口 exception.authenticationEntryPoint(new SecurityAuthenticationEntryPoint()); @@ -67,25 +72,9 @@ public class WebSecurityConfig { }) // 登录验证过滤器 .addFilterBefore(new TokenLoginFilterService(authenticationConfiguration, redisTemplate, customUserDetailsService), UsernamePasswordAuthenticationFilter.class) - // 其它权限鉴权过滤器 - .addFilterAt(new NoTokenAuthenticationFilter(redisTemplate), UsernamePasswordAuthenticationFilter.class) - .addFilterAt(new TokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) // 自定义密码加密器和用户登录 .passwordManagement(customPasswordEncoder).userDetailsService(customUserDetailsService); return httpSecurity.build(); } - - // 排出鉴定路径 - @Bean - public WebSecurityCustomizer webSecurityCustomizer() { - String[] annotations = { - "/", "/ws/**", - "/*/*/noAuth/**", "/*/noAuth/**", "/noAuth/**", - "/media.ico", "/favicon.ico", "*.html", "/webjars/**", "/v3/api-docs/**", "swagger-ui/**", - "/error", "/*/i18n/getI18n", - }; - return web -> web.ignoring().requestMatchers(annotations) - .requestMatchers(RegexRequestMatcher.regexMatcher(".*\\.(css|js)$")); - } } diff --git a/service/src/main/java/cn/bunny/services/security/filter/NoTokenAuthenticationFilter.java b/service/src/main/java/cn/bunny/services/security/filter/NoTokenAuthenticationFilter.java deleted file mode 100644 index 269300b..0000000 --- a/service/src/main/java/cn/bunny/services/security/filter/NoTokenAuthenticationFilter.java +++ /dev/null @@ -1,72 +0,0 @@ -package cn.bunny.services.security.filter; - - -import cn.bunny.common.service.context.BaseContext; -import cn.bunny.common.service.utils.JwtHelper; -import cn.bunny.common.service.utils.ResponseUtil; -import cn.bunny.dao.pojo.constant.RedisUserConstant; -import cn.bunny.dao.pojo.result.Result; -import cn.bunny.dao.pojo.result.ResultCodeEnum; -import cn.bunny.dao.vo.system.user.LoginVo; -import com.alibaba.fastjson2.JSON; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.jetbrains.annotations.NotNull; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; - -public class NoTokenAuthenticationFilter extends OncePerRequestFilter { - private final RedisTemplate redisTemplate; - - public NoTokenAuthenticationFilter(RedisTemplate redisTemplate) { - this.redisTemplate = redisTemplate; - } - - @Override - protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException { - // 判断是否有 token - String token = request.getHeader("token"); - if (token == null) { - ResponseUtil.out(response, Result.error(ResultCodeEnum.LOGIN_AUTH)); - return; - } - - // 判断 token 是否过期 - if (JwtHelper.isExpired(token)) { - ResponseUtil.out(response, Result.error(ResultCodeEnum.AUTHENTICATION_EXPIRED)); - return; - } - - // 解析JWT中的用户名 - String username = JwtHelper.getUsername(token); - Long userId = JwtHelper.getUserId(token); - - // 查找 Redis - Object loginVoObject = redisTemplate.opsForValue().get(RedisUserConstant.getAdminLoginInfoPrefix(username)); - LoginVo loginVo = JSON.parseObject(JSON.toJSONString(loginVoObject), LoginVo.class); - - // 登录信息为空 - if (loginVo == null) { - ResponseUtil.out(response, Result.error(ResultCodeEnum.LOGIN_AUTH)); - return; - } - - // 判断用户是否禁用 - if (loginVo.getStatus()) { - ResponseUtil.out(response, Result.error(ResultCodeEnum.FAIL_NO_ACCESS_DENIED_USER_LOCKED)); - return; - } - - // 设置用户信息 - BaseContext.setUsername(username); - BaseContext.setUserId(userId); - BaseContext.setLoginVo(loginVo); - - // 执行下一个过滤器 - doFilter(request, response, filterChain); - } -} \ No newline at end of file diff --git a/service/src/main/java/cn/bunny/services/security/filter/TokenAuthenticationFilter.java b/service/src/main/java/cn/bunny/services/security/filter/TokenAuthenticationFilter.java deleted file mode 100644 index b4b4b1f..0000000 --- a/service/src/main/java/cn/bunny/services/security/filter/TokenAuthenticationFilter.java +++ /dev/null @@ -1,47 +0,0 @@ -package cn.bunny.services.security.filter; - -import cn.bunny.common.service.context.BaseContext; -import cn.bunny.dao.vo.system.user.LoginVo; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.jetbrains.annotations.NotNull; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - - -public class TokenAuthenticationFilter extends OncePerRequestFilter { - - @Override - protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, FilterChain chain) throws ServletException, IOException { - // 自定义实现内容 - UsernamePasswordAuthenticationToken authentication = getAuthentication(); - SecurityContextHolder.getContext().setAuthentication(authentication); - chain.doFilter(request, response); - } - - /** - * 用户请求判断 - * - * @return 验证码方法 - */ - private UsernamePasswordAuthenticationToken getAuthentication() { - // 请求头是否有token - LoginVo loginVo = BaseContext.getLoginVo(); - String username = loginVo.getUsername(); - List roleList = loginVo.getRoles(); - - // 角色列表 - List authList = roleList.stream().map(SimpleGrantedAuthority::new).toList(); - - if (authList.isEmpty()) return new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>()); - return new UsernamePasswordAuthenticationToken(username, null, authList); - } -} diff --git a/service/src/main/java/cn/bunny/services/security/handelr/SecurityAuthenticationEntryPoint.java b/service/src/main/java/cn/bunny/services/security/handelr/SecurityAuthenticationEntryPoint.java index 5f60286..b45d885 100644 --- a/service/src/main/java/cn/bunny/services/security/handelr/SecurityAuthenticationEntryPoint.java +++ b/service/src/main/java/cn/bunny/services/security/handelr/SecurityAuthenticationEntryPoint.java @@ -16,7 +16,7 @@ import org.springframework.security.web.AuthenticationEntryPoint; public class SecurityAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) { - String token = response.getHeader("token"); + String token = request.getHeader("token"); String message = authException.getMessage(); // 创建结果对象 Result result; diff --git a/service/src/main/java/cn/bunny/services/security/service/impl/CustomAuthorizationManagerServiceImpl.java b/service/src/main/java/cn/bunny/services/security/service/impl/CustomAuthorizationManagerServiceImpl.java index 0c0cd5c..aee126e 100644 --- a/service/src/main/java/cn/bunny/services/security/service/impl/CustomAuthorizationManagerServiceImpl.java +++ b/service/src/main/java/cn/bunny/services/security/service/impl/CustomAuthorizationManagerServiceImpl.java @@ -1,15 +1,20 @@ package cn.bunny.services.security.service.impl; import cn.bunny.common.service.context.BaseContext; +import cn.bunny.common.service.utils.JwtHelper; import cn.bunny.dao.entity.system.Power; import cn.bunny.dao.entity.system.Role; +import cn.bunny.dao.pojo.constant.RedisUserConstant; +import cn.bunny.dao.vo.system.user.LoginVo; import cn.bunny.services.mapper.PowerMapper; import cn.bunny.services.mapper.RoleMapper; import cn.bunny.services.security.custom.CustomCheckIsAdmin; +import com.alibaba.fastjson2.JSON; import jakarta.servlet.http.HttpServletRequest; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.core.Authentication; @@ -36,12 +41,49 @@ public class CustomAuthorizationManagerServiceImpl implements AuthorizationManag @Autowired private RoleMapper roleMapper; + @Autowired + private RedisTemplate redisTemplate; + @SneakyThrows @Override public AuthorizationDecision check(Supplier authentication, RequestAuthorizationContext context) { // 用户的token和用户id、请求Url HttpServletRequest request = context.getRequest(); + // 判断是否有 token + String token = request.getHeader("token"); + if (token == null) { + return new AuthorizationDecision(false); + } + + // 判断 token 是否过期 + if (JwtHelper.isExpired(token)) { + return new AuthorizationDecision(false); + } + + // 解析JWT中的用户名 + String username = JwtHelper.getUsername(token); + Long userId = JwtHelper.getUserId(token); + + // 查找 Redis + Object loginVoObject = redisTemplate.opsForValue().get(RedisUserConstant.getAdminLoginInfoPrefix(username)); + LoginVo loginVo = JSON.parseObject(JSON.toJSONString(loginVoObject), LoginVo.class); + + // 登录信息为空 + if (loginVo == null) { + return new AuthorizationDecision(false); + } + + // 判断用户是否禁用 + if (loginVo.getStatus()) { + return new AuthorizationDecision(false); + } + + // 设置用户信息 + BaseContext.setUsername(username); + BaseContext.setUserId(userId); + BaseContext.setLoginVo(loginVo); + // 校验权限 return new AuthorizationDecision(hasAuth(request)); }