Commit fc8eac548a698174554892f6bba8ca9758cf34f9
1 parent
ee9a358b
集成 Spring Security 组件,重构后,整体逻辑更加清晰
Showing
27 changed files
with
1821 additions
and
177 deletions
http-client.env.json
| 1 | 1 | { |
| 2 | 2 | "local": { |
| 3 | 3 | "baseUrl": "http://127.0.0.1:8080/api", |
| 4 | - "token": "eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjE2MDk2ODE2MzEsInN1YiI6ImE3ZGE1MWE2YWUyYTQxOWRhNmExYTlkYmJiMTVmZjc4In0.RXG7alSz64lE9oPSgbnYT_KsX7kvoHVhF5oHxXHztr1KjsttOqOppSmHGBYFI7Y75bsjEBSxSqbGsS1O1S2b1w" | |
| 4 | + "token": "yudaoyuanma1" | |
| 5 | 5 | } |
| 6 | 6 | } | ... | ... |
ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java renamed to src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java
| 1 | -package com.ruoyi.framework.config; | |
| 2 | - | |
| 3 | -import org.springframework.beans.factory.annotation.Autowired; | |
| 4 | -import org.springframework.context.annotation.Bean; | |
| 5 | -import org.springframework.http.HttpMethod; | |
| 6 | -import org.springframework.security.authentication.AuthenticationManager; | |
| 7 | -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; | |
| 8 | -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; | |
| 9 | -import org.springframework.security.config.annotation.web.builders.HttpSecurity; | |
| 10 | -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; | |
| 11 | -import org.springframework.security.config.http.SessionCreationPolicy; | |
| 12 | -import org.springframework.security.core.userdetails.UserDetailsService; | |
| 13 | -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | |
| 14 | -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | |
| 15 | -import org.springframework.security.web.authentication.logout.LogoutFilter; | |
| 16 | -import org.springframework.web.filter.CorsFilter; | |
| 17 | -import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter; | |
| 18 | -import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl; | |
| 19 | -import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl; | |
| 20 | - | |
| 21 | -/** | |
| 22 | - * spring security配置 | |
| 23 | - * | |
| 24 | - * @author ruoyi | |
| 25 | - */ | |
| 26 | -@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) | |
| 27 | -public class SecurityConfig extends WebSecurityConfigurerAdapter | |
| 28 | -{ | |
| 29 | - /** | |
| 30 | - * 自定义用户认证逻辑 | |
| 31 | - */ | |
| 32 | - @Autowired | |
| 33 | - private UserDetailsService userDetailsService; | |
| 34 | - | |
| 35 | - /** | |
| 36 | - * 认证失败处理类 | |
| 37 | - */ | |
| 38 | - @Autowired | |
| 39 | - private AuthenticationEntryPointImpl unauthorizedHandler; | |
| 40 | - | |
| 41 | - /** | |
| 42 | - * 退出处理类 | |
| 43 | - */ | |
| 44 | - @Autowired | |
| 45 | - private LogoutSuccessHandlerImpl logoutSuccessHandler; | |
| 46 | - | |
| 47 | - /** | |
| 48 | - * token认证过滤器 | |
| 49 | - */ | |
| 50 | - @Autowired | |
| 51 | - private JwtAuthenticationTokenFilter authenticationTokenFilter; | |
| 52 | - | |
| 53 | - /** | |
| 54 | - * 跨域过滤器 | |
| 55 | - */ | |
| 56 | - @Autowired | |
| 57 | - private CorsFilter corsFilter; | |
| 58 | - | |
| 59 | - /** | |
| 60 | - * 解决 无法直接注入 AuthenticationManager | |
| 61 | - * | |
| 62 | - * @return | |
| 63 | - * @throws Exception | |
| 64 | - */ | |
| 65 | - @Bean | |
| 66 | - @Override | |
| 67 | - public AuthenticationManager authenticationManagerBean() throws Exception | |
| 68 | - { | |
| 69 | - return super.authenticationManagerBean(); | |
| 70 | - } | |
| 71 | - | |
| 72 | - /** | |
| 73 | - * anyRequest | 匹配所有请求路径 | |
| 74 | - * access | SpringEl表达式结果为true时可以访问 | |
| 75 | - * anonymous | 匿名可以访问 | |
| 76 | - * denyAll | 用户不能访问 | |
| 77 | - * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) | |
| 78 | - * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 | |
| 79 | - * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 | |
| 80 | - * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 | |
| 81 | - * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 | |
| 82 | - * hasRole | 如果有参数,参数表示角色,则其角色可以访问 | |
| 83 | - * permitAll | 用户可以任意访问 | |
| 84 | - * rememberMe | 允许通过remember-me登录的用户访问 | |
| 85 | - * authenticated | 用户登录后可访问 | |
| 86 | - */ | |
| 87 | - @Override | |
| 88 | - protected void configure(HttpSecurity httpSecurity) throws Exception | |
| 89 | - { | |
| 90 | - httpSecurity | |
| 91 | - // CSRF禁用,因为不使用session | |
| 92 | - .csrf().disable() | |
| 93 | - // 认证失败处理类 | |
| 94 | - .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() | |
| 95 | - // 基于token,所以不需要session | |
| 96 | - .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() | |
| 97 | - // 过滤请求 | |
| 98 | - .authorizeRequests() | |
| 99 | - // 对于登录login 验证码captchaImage 允许匿名访问 | |
| 100 | - .antMatchers("/login", "/captchaImage").anonymous() | |
| 101 | - .antMatchers( | |
| 102 | - HttpMethod.GET, | |
| 103 | - "/*.html", | |
| 104 | - "/**/*.html", | |
| 105 | - "/**/*.css", | |
| 106 | - "/**/*.js" | |
| 107 | - ).permitAll() | |
| 108 | - .antMatchers("/profile/**").anonymous() | |
| 109 | - .antMatchers("/common/download**").anonymous() | |
| 110 | - .antMatchers("/common/download/resource**").anonymous() | |
| 111 | - .antMatchers("/swagger-ui.html").anonymous() | |
| 112 | - .antMatchers("/swagger-resources/**").anonymous() | |
| 113 | - .antMatchers("/webjars/**").anonymous() | |
| 114 | - .antMatchers("/*/api-docs").anonymous() | |
| 115 | - .antMatchers("/druid/**").anonymous() | |
| 116 | - // 除上面外的所有请求全部需要鉴权认证 | |
| 117 | - .anyRequest().authenticated() | |
| 118 | - .and() | |
| 119 | - .headers().frameOptions().disable(); | |
| 120 | - httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); | |
| 121 | - // 添加JWT filter | |
| 122 | - httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); | |
| 123 | - // 添加CORS filter | |
| 124 | - httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); | |
| 125 | - httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); | |
| 126 | - } | |
| 127 | - | |
| 128 | - | |
| 129 | - /** | |
| 130 | - * 强散列哈希加密实现 | |
| 131 | - */ | |
| 132 | - @Bean | |
| 133 | - public BCryptPasswordEncoder bCryptPasswordEncoder() | |
| 134 | - { | |
| 135 | - return new BCryptPasswordEncoder(); | |
| 136 | - } | |
| 137 | - | |
| 138 | - /** | |
| 139 | - * 身份认证接口 | |
| 140 | - */ | |
| 141 | - @Override | |
| 142 | - protected void configure(AuthenticationManagerBuilder auth) throws Exception | |
| 143 | - { | |
| 144 | - auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); | |
| 145 | - } | |
| 146 | -} | |
| 1 | +package cn.iocoder.dashboard.framework.security.config; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.security.core.filter.JwtAuthenticationTokenFilter; | |
| 4 | +import cn.iocoder.dashboard.framework.security.core.handler.AuthenticationEntryPointImpl; | |
| 5 | +import cn.iocoder.dashboard.framework.security.core.handler.LogoutSuccessHandlerImpl; | |
| 6 | +import cn.iocoder.dashboard.framework.web.config.WebProperties; | |
| 7 | +import org.springframework.boot.context.properties.EnableConfigurationProperties; | |
| 8 | +import org.springframework.context.annotation.Bean; | |
| 9 | +import org.springframework.http.HttpMethod; | |
| 10 | +import org.springframework.security.authentication.AuthenticationManager; | |
| 11 | +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; | |
| 12 | +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; | |
| 13 | +import org.springframework.security.config.annotation.web.builders.HttpSecurity; | |
| 14 | +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; | |
| 15 | +import org.springframework.security.config.http.SessionCreationPolicy; | |
| 16 | +import org.springframework.security.core.userdetails.UserDetailsService; | |
| 17 | +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | |
| 18 | +import org.springframework.security.crypto.password.PasswordEncoder; | |
| 19 | +import org.springframework.security.web.AuthenticationEntryPoint; | |
| 20 | +import org.springframework.security.web.access.AccessDeniedHandler; | |
| 21 | +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | |
| 22 | +import org.springframework.security.web.authentication.logout.LogoutFilter; | |
| 23 | +import org.springframework.web.filter.CorsFilter; | |
| 24 | + | |
| 25 | +import javax.annotation.Resource; | |
| 26 | + | |
| 27 | +/** | |
| 28 | + * spring security配置 | |
| 29 | + * | |
| 30 | + * @author ruoyi | |
| 31 | + */ | |
| 32 | +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) | |
| 33 | +@EnableConfigurationProperties(SecurityProperties.class) | |
| 34 | +public class SecurityConfiguration extends WebSecurityConfigurerAdapter { | |
| 35 | + | |
| 36 | + /** | |
| 37 | + * 自定义用户认证逻辑 | |
| 38 | + */ | |
| 39 | + @Resource | |
| 40 | + private UserDetailsService userDetailsService; | |
| 41 | + | |
| 42 | + /** | |
| 43 | + * 认证失败处理类 | |
| 44 | + */ | |
| 45 | + @Resource | |
| 46 | + private AuthenticationEntryPoint unauthorizedHandler; | |
| 47 | + /** | |
| 48 | + * 权限不够处理器 | |
| 49 | + */ | |
| 50 | + @Resource | |
| 51 | + private AccessDeniedHandler accessDeniedHandler; | |
| 52 | + /** | |
| 53 | + * 退出处理类 | |
| 54 | + */ | |
| 55 | + @Resource | |
| 56 | + private LogoutSuccessHandlerImpl logoutSuccessHandler; | |
| 57 | + | |
| 58 | + /** | |
| 59 | + * Token 认证过滤器 | |
| 60 | + */ | |
| 61 | + @Resource | |
| 62 | + private JwtAuthenticationTokenFilter authenticationTokenFilter; | |
| 63 | + | |
| 64 | + @Resource | |
| 65 | + private WebProperties webProperties; | |
| 66 | + | |
| 67 | + /** | |
| 68 | + * 由于 Spring Security 创建 AuthenticationManager 对象时,没声明 @Bean 注解,导致无法被注入 | |
| 69 | + * 通过覆写父类的该方法,添加 @Bean 注解,解决该问题 | |
| 70 | + */ | |
| 71 | + @Bean | |
| 72 | + @Override | |
| 73 | + public AuthenticationManager authenticationManagerBean() throws Exception { | |
| 74 | + return super.authenticationManagerBean(); | |
| 75 | + } | |
| 76 | + | |
| 77 | + /** | |
| 78 | + * Spring Security 加密器 | |
| 79 | + * 考虑到安全性,这里采用 BCryptPasswordEncoder 加密器 | |
| 80 | + * | |
| 81 | + * @see <a href="http://stackabuse.com/password-encoding-with-spring-security/">Password Encoding with Spring Security</a> | |
| 82 | + */ | |
| 83 | + @Bean | |
| 84 | + public PasswordEncoder passwordEncoder() { | |
| 85 | + return new BCryptPasswordEncoder(); | |
| 86 | + } | |
| 87 | + | |
| 88 | + /** | |
| 89 | + * 身份认证接口 | |
| 90 | + */ | |
| 91 | + @Override | |
| 92 | + protected void configure(AuthenticationManagerBuilder auth) throws Exception { | |
| 93 | + auth.userDetailsService(userDetailsService) | |
| 94 | + .passwordEncoder(passwordEncoder()); | |
| 95 | + } | |
| 96 | + | |
| 97 | + /** | |
| 98 | + * 配置 URL 的安全配置 | |
| 99 | + * | |
| 100 | + * anyRequest | 匹配所有请求路径 | |
| 101 | + * access | SpringEl表达式结果为true时可以访问 | |
| 102 | + * anonymous | 匿名可以访问 | |
| 103 | + * denyAll | 用户不能访问 | |
| 104 | + * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) | |
| 105 | + * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 | |
| 106 | + * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 | |
| 107 | + * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 | |
| 108 | + * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 | |
| 109 | + * hasRole | 如果有参数,参数表示角色,则其角色可以访问 | |
| 110 | + * permitAll | 用户可以任意访问 | |
| 111 | + * rememberMe | 允许通过remember-me登录的用户访问 | |
| 112 | + * authenticated | 用户登录后可访问 | |
| 113 | + */ | |
| 114 | + @Override | |
| 115 | + protected void configure(HttpSecurity httpSecurity) throws Exception { | |
| 116 | + httpSecurity | |
| 117 | + // CSRF 禁用,因为不使用 Session | |
| 118 | + .csrf().disable() | |
| 119 | + // 基于 token 机制,所以不需要 Session | |
| 120 | + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() | |
| 121 | + // 一堆自定义的 Spring Security 处理器 | |
| 122 | + .exceptionHandling().authenticationEntryPoint(unauthorizedHandler) | |
| 123 | + .accessDeniedHandler(accessDeniedHandler).and() | |
| 124 | + // TODO 过滤请求 | |
| 125 | + .authorizeRequests() | |
| 126 | + // 登陆的接口,可匿名访问 | |
| 127 | + .antMatchers(webProperties.getApiPrefix() + "/login").anonymous() | |
| 128 | + // 通用的接口,可匿名访问 | |
| 129 | + .antMatchers( webProperties.getApiPrefix() + "/captcha/**").anonymous() | |
| 130 | + // TODO | |
| 131 | + .antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll() | |
| 132 | + .antMatchers("/profile/**").anonymous() | |
| 133 | + .antMatchers("/common/download**").anonymous() | |
| 134 | + .antMatchers("/common/download/resource**").anonymous() | |
| 135 | + .antMatchers("/swagger-ui.html").anonymous() | |
| 136 | + .antMatchers("/swagger-resources/**").anonymous() | |
| 137 | + .antMatchers("/webjars/**").anonymous() | |
| 138 | + .antMatchers("/*/api-docs").anonymous() | |
| 139 | + .antMatchers("/druid/**").hasAnyAuthority("druid") // TODO 芋艿,未来需要在拓展下 | |
| 140 | + // 除上面外的所有请求全部需要鉴权认证 | |
| 141 | + .anyRequest().authenticated() | |
| 142 | + .and() | |
| 143 | + .headers().frameOptions().disable(); | |
| 144 | + httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); | |
| 145 | + // 添加 JWT Filter | |
| 146 | + httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); | |
| 147 | + } | |
| 148 | + | |
| 149 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityProperties.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.security.config; | |
| 2 | + | |
| 3 | +import lombok.Data; | |
| 4 | +import org.springframework.boot.context.properties.ConfigurationProperties; | |
| 5 | +import org.springframework.validation.annotation.Validated; | |
| 6 | + | |
| 7 | +import javax.validation.Valid; | |
| 8 | +import javax.validation.constraints.NotEmpty; | |
| 9 | +import javax.validation.constraints.NotNull; | |
| 10 | +import java.time.Duration; | |
| 11 | + | |
| 12 | +@ConfigurationProperties(prefix = "yudao.security") | |
| 13 | +@Validated | |
| 14 | +@Data | |
| 15 | +public class SecurityProperties { | |
| 16 | + | |
| 17 | + /** | |
| 18 | + * HTTP 请求时,访问令牌的请求 Header | |
| 19 | + */ | |
| 20 | + @NotEmpty(message = "Token Header 不能为空") | |
| 21 | + private String tokenHeader; | |
| 22 | + /** | |
| 23 | + * Token 过期时间 | |
| 24 | + */ | |
| 25 | + @NotNull(message = "Token 过期时间不能为空") | |
| 26 | + private Duration tokenTimeout; | |
| 27 | + /** | |
| 28 | + * Token 秘钥 | |
| 29 | + */ | |
| 30 | + @NotEmpty(message = "Token 秘钥不能为空") | |
| 31 | + private String tokenSecret; | |
| 32 | + /** | |
| 33 | + * Session 过期时间 | |
| 34 | + * | |
| 35 | + * 当 User 用户超过当前时间未操作,则 Session 会过期 | |
| 36 | + */ | |
| 37 | + @NotNull(message = "Session 过期时间不能为空") | |
| 38 | + private Duration sessionTimeout; | |
| 39 | + | |
| 40 | + /** | |
| 41 | + * mock 模式的开关 | |
| 42 | + */ | |
| 43 | + @NotNull(message = "mock 模式的开关不能为空") | |
| 44 | + private Boolean mockEnable; | |
| 45 | + /** | |
| 46 | + * mock 模式的秘钥 | |
| 47 | + * 一定要配置秘钥,保证安全性 | |
| 48 | + */ | |
| 49 | + @NotEmpty(message = "mock 模式的秘钥不能为空") // 这里设置了一个默认值,因为实际上只有 mockEnable 为 true 时才需要配置。 | |
| 50 | + private String mockSecret = "yudaoyuanma"; | |
| 51 | + | |
| 52 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/security/core/LoginUser.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.security.core; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.modules.system.enums.user.UserStatus; | |
| 4 | +import com.alibaba.fastjson.annotation.JSONField; | |
| 5 | +import lombok.Data; | |
| 6 | +import org.springframework.data.annotation.Transient; | |
| 7 | +import org.springframework.security.core.GrantedAuthority; | |
| 8 | +import org.springframework.security.core.userdetails.UserDetails; | |
| 9 | + | |
| 10 | +import java.util.Collection; | |
| 11 | +import java.util.Date; | |
| 12 | +import java.util.Set; | |
| 13 | + | |
| 14 | +/** | |
| 15 | + * 登陆用户信息 | |
| 16 | + * | |
| 17 | + * @author 芋道源码 | |
| 18 | + */ | |
| 19 | +@Data | |
| 20 | +public class LoginUser implements UserDetails { | |
| 21 | + | |
| 22 | + /** | |
| 23 | + * 用户编号 | |
| 24 | + */ | |
| 25 | + private Long userId; | |
| 26 | + /** | |
| 27 | + * 角色编号数组 | |
| 28 | + */ | |
| 29 | + private Set<Integer> roleIds; | |
| 30 | + /** | |
| 31 | + * 最后更新时间 | |
| 32 | + */ | |
| 33 | + private Date updateTime; | |
| 34 | + | |
| 35 | + /** | |
| 36 | + * 用户名 | |
| 37 | + */ | |
| 38 | + private String username; | |
| 39 | + /** | |
| 40 | + * 密码 | |
| 41 | + */ | |
| 42 | + private String password; | |
| 43 | + /** | |
| 44 | + * 状态 | |
| 45 | + */ | |
| 46 | + private String status; | |
| 47 | + | |
| 48 | + @Override | |
| 49 | + @JSONField(serialize = false) // 避免序列化 | |
| 50 | + public String getPassword() { | |
| 51 | + return password; | |
| 52 | + } | |
| 53 | + | |
| 54 | + @Override | |
| 55 | + @JSONField(serialize = false) // 避免序列化 | |
| 56 | + public String getUsername() { | |
| 57 | + return username; | |
| 58 | + } | |
| 59 | + | |
| 60 | + @Override | |
| 61 | + @JSONField(serialize = false) // 避免序列化 | |
| 62 | + public boolean isEnabled() { | |
| 63 | + return UserStatus.OK.getCode().equals(status); | |
| 64 | + } | |
| 65 | + | |
| 66 | + @Override | |
| 67 | + @JSONField(serialize = false) // 避免序列化 | |
| 68 | + public Collection<? extends GrantedAuthority> getAuthorities() { | |
| 69 | + return null; | |
| 70 | + } | |
| 71 | + | |
| 72 | + @Override | |
| 73 | + @JSONField(serialize = false) // 避免序列化 | |
| 74 | + public boolean isAccountNonExpired() { | |
| 75 | + return true; // 返回 true,不依赖 Spring Security 判断 | |
| 76 | + } | |
| 77 | + | |
| 78 | + @Override | |
| 79 | + @JSONField(serialize = false) // 避免序列化 | |
| 80 | + public boolean isAccountNonLocked() { | |
| 81 | + return true; // 返回 true,不依赖 Spring Security 判断 | |
| 82 | + } | |
| 83 | + | |
| 84 | + @Override | |
| 85 | + @JSONField(serialize = false) // 避免序列化 | |
| 86 | + public boolean isCredentialsNonExpired() { | |
| 87 | + return true; // 返回 true,不依赖 Spring Security 判断 | |
| 88 | + } | |
| 89 | + | |
| 90 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/security/core/filter/JwtAuthenticationTokenFilter.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.security.core.filter; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.StrUtil; | |
| 4 | +import cn.hutool.extra.servlet.ServletUtil; | |
| 5 | +import cn.iocoder.dashboard.common.pojo.CommonResult; | |
| 6 | +import cn.iocoder.dashboard.framework.security.config.SecurityProperties; | |
| 7 | +import cn.iocoder.dashboard.framework.security.core.LoginUser; | |
| 8 | +import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils; | |
| 9 | +import cn.iocoder.dashboard.framework.web.core.handler.GlobalExceptionHandler; | |
| 10 | +import cn.iocoder.dashboard.modules.system.service.auth.SysAuthService; | |
| 11 | +import cn.iocoder.dashboard.util.servlet.ServletUtils; | |
| 12 | +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | |
| 13 | +import org.springframework.security.core.context.SecurityContextHolder; | |
| 14 | +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; | |
| 15 | +import org.springframework.stereotype.Component; | |
| 16 | +import org.springframework.web.filter.OncePerRequestFilter; | |
| 17 | + | |
| 18 | +import javax.annotation.Resource; | |
| 19 | +import javax.servlet.FilterChain; | |
| 20 | +import javax.servlet.ServletException; | |
| 21 | +import javax.servlet.http.HttpServletRequest; | |
| 22 | +import javax.servlet.http.HttpServletResponse; | |
| 23 | +import java.io.IOException; | |
| 24 | + | |
| 25 | +/** | |
| 26 | + * JWT 过滤器,验证 token 的有效性 | |
| 27 | + * 验证通过后,获得 {@link LoginUser} 信息,并加入到 Spring Security 上下文 | |
| 28 | + * | |
| 29 | + * @author ruoyi | |
| 30 | + */ | |
| 31 | +@Component | |
| 32 | +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { | |
| 33 | + | |
| 34 | + @Resource | |
| 35 | + private SecurityProperties securityProperties; | |
| 36 | + @Resource | |
| 37 | + private SysAuthService authService; | |
| 38 | + @Resource | |
| 39 | + private GlobalExceptionHandler globalExceptionHandler; | |
| 40 | + | |
| 41 | + @Override | |
| 42 | + @SuppressWarnings("NullableProblems") | |
| 43 | + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) | |
| 44 | + throws ServletException, IOException { | |
| 45 | + String token = SecurityUtils.obtainAuthorization(request, securityProperties.getTokenHeader()); | |
| 46 | + if (StrUtil.isNotEmpty(token)) { | |
| 47 | + try { | |
| 48 | + // 验证 token 有效性 | |
| 49 | + LoginUser loginUser = authService.verifyTokenAndRefresh(token); | |
| 50 | + // 模拟 Login 功能,方便日常开发调试 | |
| 51 | + if (loginUser == null) { | |
| 52 | + loginUser = this.mockLoginUser(token); | |
| 53 | + } | |
| 54 | + // 设置当前用户 | |
| 55 | + if (loginUser != null) { | |
| 56 | + SecurityUtils.setLoginUser(loginUser, request); | |
| 57 | + } | |
| 58 | + } catch (Throwable ex) { | |
| 59 | + CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex); | |
| 60 | + ServletUtils.writeJSON(response, result); | |
| 61 | + return; | |
| 62 | + } | |
| 63 | + } | |
| 64 | + | |
| 65 | + // 继续过滤链 | |
| 66 | + chain.doFilter(request, response); | |
| 67 | + } | |
| 68 | + | |
| 69 | + /** | |
| 70 | + * 模拟登陆用户,方便日常开发调试 | |
| 71 | + * | |
| 72 | + * 注意,在线上环境下,一定要关闭该功能!!! | |
| 73 | + * | |
| 74 | + * @param token 模拟的 token,格式为 {@link SecurityProperties#getTokenSecret()} + 用户编号 | |
| 75 | + * @return 模拟的 LoginUser | |
| 76 | + */ | |
| 77 | + private LoginUser mockLoginUser(String token) { | |
| 78 | + if (!securityProperties.getMockEnable()) { | |
| 79 | + return null; | |
| 80 | + } | |
| 81 | + // 必须以 mockSecret 开头 | |
| 82 | + if (!token.startsWith(securityProperties.getMockSecret())) { | |
| 83 | + return null; | |
| 84 | + } | |
| 85 | + Long userId = Long.valueOf(token.substring(securityProperties.getMockSecret().length())); | |
| 86 | + return authService.mockLogin(userId); | |
| 87 | + } | |
| 88 | + | |
| 89 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/security/core/handler/AccessDeniedHandlerImpl.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.security.core.handler; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | |
| 4 | +import cn.iocoder.dashboard.common.pojo.CommonResult; | |
| 5 | +import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils; | |
| 6 | +import cn.iocoder.dashboard.util.servlet.ServletUtils; | |
| 7 | +import lombok.extern.slf4j.Slf4j; | |
| 8 | +import org.springframework.security.access.AccessDeniedException; | |
| 9 | +import org.springframework.security.core.AuthenticationException; | |
| 10 | +import org.springframework.security.web.access.AccessDeniedHandler; | |
| 11 | +import org.springframework.security.web.access.ExceptionTranslationFilter; | |
| 12 | +import org.springframework.stereotype.Component; | |
| 13 | + | |
| 14 | +import javax.servlet.FilterChain; | |
| 15 | +import javax.servlet.ServletException; | |
| 16 | +import javax.servlet.http.HttpServletRequest; | |
| 17 | +import javax.servlet.http.HttpServletResponse; | |
| 18 | +import java.io.IOException; | |
| 19 | + | |
| 20 | +import static cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants.UNAUTHORIZED; | |
| 21 | + | |
| 22 | +/** | |
| 23 | + * 访问一个需要认证的 URL 资源,已经认证(登录)但是没有权限的情况下,返回 {@link GlobalErrorCodeConstants#FORBIDDEN} 错误码。 | |
| 24 | + * | |
| 25 | + * 补充:Spring Security 通过 {@link ExceptionTranslationFilter#handleAccessDeniedException(HttpServletRequest, HttpServletResponse, FilterChain, AccessDeniedException)} 方法,调用当前类 | |
| 26 | + * | |
| 27 | + * @author 芋道源码 | |
| 28 | + */ | |
| 29 | +@Component | |
| 30 | +@Slf4j | |
| 31 | +@SuppressWarnings("JavadocReference") | |
| 32 | +public class AccessDeniedHandlerImpl implements AccessDeniedHandler { | |
| 33 | + | |
| 34 | + @Override | |
| 35 | + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) | |
| 36 | + throws IOException, ServletException { | |
| 37 | + // 打印 warn 的原因是,不定期合并 warn,看看有没恶意破坏 | |
| 38 | + log.warn("[commence][访问 URL({}) 时,用户({}) 权限不够]", request.getRequestURI(), | |
| 39 | + SecurityUtils.getLoginUser().getUserId(), e); | |
| 40 | + // 返回 403 | |
| 41 | + ServletUtils.writeJSON(response, CommonResult.error(UNAUTHORIZED)); | |
| 42 | + } | |
| 43 | + | |
| 44 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/security/core/handler/AuthenticationEntryPointImpl.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.security.core.handler; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants; | |
| 4 | +import cn.iocoder.dashboard.common.pojo.CommonResult; | |
| 5 | +import cn.iocoder.dashboard.util.servlet.ServletUtils; | |
| 6 | +import lombok.extern.slf4j.Slf4j; | |
| 7 | +import org.springframework.security.core.AuthenticationException; | |
| 8 | +import org.springframework.security.web.AuthenticationEntryPoint; | |
| 9 | +import org.springframework.security.web.access.ExceptionTranslationFilter; | |
| 10 | +import org.springframework.stereotype.Component; | |
| 11 | + | |
| 12 | +import javax.servlet.FilterChain; | |
| 13 | +import javax.servlet.http.HttpServletRequest; | |
| 14 | +import javax.servlet.http.HttpServletResponse; | |
| 15 | + | |
| 16 | +import static cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants.UNAUTHORIZED; | |
| 17 | + | |
| 18 | +/** | |
| 19 | + * 访问一个需要认证的 URL 资源,但是此时自己尚未认证(登录)的情况下,返回 {@link GlobalErrorCodeConstants#UNAUTHORIZED} 错误码,从而使前端重定向到登录页 | |
| 20 | + * | |
| 21 | + * 补充:Spring Security 通过 {@link ExceptionTranslationFilter#sendStartAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, AuthenticationException)} 方法,调用当前类 | |
| 22 | + * | |
| 23 | + * @author ruoyi | |
| 24 | + */ | |
| 25 | +@Component | |
| 26 | +@Slf4j | |
| 27 | +@SuppressWarnings("JavadocReference") // 忽略文档引用报错 | |
| 28 | +public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint { | |
| 29 | + | |
| 30 | + @Override | |
| 31 | + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) { | |
| 32 | + log.debug("[commence][访问 URL({}) 时,没有登录]", request.getRequestURI(), e); | |
| 33 | + // 返回 401 | |
| 34 | + ServletUtils.writeJSON(response, CommonResult.error(UNAUTHORIZED)); | |
| 35 | + } | |
| 36 | + | |
| 37 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/security/core/handler/LogoutSuccessHandlerImpl.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.security.core.handler; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.StrUtil; | |
| 4 | +import cn.iocoder.dashboard.framework.security.config.SecurityProperties; | |
| 5 | +import cn.iocoder.dashboard.framework.security.core.service.SecurityFrameworkService; | |
| 6 | +import cn.iocoder.dashboard.framework.security.core.util.SecurityUtils; | |
| 7 | +import cn.iocoder.dashboard.util.servlet.ServletUtils; | |
| 8 | +import org.springframework.security.core.Authentication; | |
| 9 | +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; | |
| 10 | +import org.springframework.stereotype.Component; | |
| 11 | + | |
| 12 | +import javax.annotation.Resource; | |
| 13 | +import javax.servlet.http.HttpServletRequest; | |
| 14 | +import javax.servlet.http.HttpServletResponse; | |
| 15 | + | |
| 16 | + | |
| 17 | +/** | |
| 18 | + * 自定义退出处理器 | |
| 19 | + * | |
| 20 | + * @author ruoyi | |
| 21 | + */ | |
| 22 | +@Component | |
| 23 | +public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler { | |
| 24 | + | |
| 25 | + @Resource | |
| 26 | + private SecurityProperties securityProperties; | |
| 27 | + | |
| 28 | + @Resource | |
| 29 | + private SecurityFrameworkService securityFrameworkService; | |
| 30 | + | |
| 31 | + @Override | |
| 32 | + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { | |
| 33 | + // 执行退出 | |
| 34 | + String token = SecurityUtils.obtainAuthorization(request, securityProperties.getTokenHeader()); | |
| 35 | + if (StrUtil.isNotBlank(token)) { | |
| 36 | + securityFrameworkService.logout(token); | |
| 37 | + } | |
| 38 | + // 返回成功 | |
| 39 | + ServletUtils.writeJSON(response, null); | |
| 40 | +// ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(HttpStatus.OK.value(), "退出成功"))); | |
| 41 | + } | |
| 42 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/security/core/service/SecurityFrameworkService.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.security.core.service; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.security.core.LoginUser; | |
| 4 | +import org.springframework.security.core.userdetails.UserDetailsService; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * Security 框架 Service 接口,定义 security 组件需要的功能 | |
| 8 | + */ | |
| 9 | +public interface SecurityFrameworkService extends UserDetailsService { | |
| 10 | + | |
| 11 | + /** | |
| 12 | + * 基于 token 退出登录 | |
| 13 | + * | |
| 14 | + * @param token token | |
| 15 | + */ | |
| 16 | + void logout(String token); | |
| 17 | + | |
| 18 | + /** | |
| 19 | + * 校验 token 的有效性,并获取用户信息 | |
| 20 | + * 通过后,刷新 token 的过期时间 | |
| 21 | + * | |
| 22 | + * @param token token | |
| 23 | + * @return 用户信息 | |
| 24 | + */ | |
| 25 | + LoginUser verifyTokenAndRefresh(String token); | |
| 26 | + | |
| 27 | + /** | |
| 28 | + * 模拟指定用户编号的 LoginUser | |
| 29 | + * | |
| 30 | + * @param userId 用户编号 | |
| 31 | + * @return 登录用户 | |
| 32 | + */ | |
| 33 | + LoginUser mockLogin(Long userId); | |
| 34 | + | |
| 35 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/security/core/util/SecurityUtils.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.security.core.util; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.security.core.LoginUser; | |
| 4 | +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | |
| 5 | +import org.springframework.security.core.context.SecurityContextHolder; | |
| 6 | +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; | |
| 7 | +import org.springframework.util.StringUtils; | |
| 8 | + | |
| 9 | +import javax.servlet.http.HttpServletRequest; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * 安全服务工具类 | |
| 13 | + * | |
| 14 | + * @author ruoyi | |
| 15 | + */ | |
| 16 | +public class SecurityUtils { | |
| 17 | + | |
| 18 | + /** | |
| 19 | + * 从请求中,获得认证 Token | |
| 20 | + * | |
| 21 | + * @param request 请求 | |
| 22 | + * @param header 认证 Token 对应的 Header 名字 | |
| 23 | + * @return 认证 Token | |
| 24 | + */ | |
| 25 | + public static String obtainAuthorization(HttpServletRequest request, String header) { | |
| 26 | + String authorization = request.getHeader(header); | |
| 27 | + if (!StringUtils.hasText(authorization)) { | |
| 28 | + return null; | |
| 29 | + } | |
| 30 | + int index = authorization.indexOf("Bearer "); | |
| 31 | + if (index == -1) { // 未找到 | |
| 32 | + return null; | |
| 33 | + } | |
| 34 | + return authorization.substring(index + 7).trim(); | |
| 35 | + } | |
| 36 | + | |
| 37 | + /** | |
| 38 | + * 获取当前用户 | |
| 39 | + */ | |
| 40 | + public static LoginUser getLoginUser() { | |
| 41 | + return (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); | |
| 42 | + } | |
| 43 | + | |
| 44 | + /** | |
| 45 | + * 设置当前用户 | |
| 46 | + * | |
| 47 | + * @param loginUser 登陆用户 | |
| 48 | + * @param request 请求 | |
| 49 | + */ | |
| 50 | + public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) { | |
| 51 | + // 创建 UsernamePasswordAuthenticationToken 对象 | |
| 52 | + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( | |
| 53 | + loginUser, null, null); | |
| 54 | + authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); | |
| 55 | + // 设置到上下文 | |
| 56 | + SecurityContextHolder.getContext().setAuthentication(authenticationToken); | |
| 57 | + } | |
| 58 | + | |
| 59 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/security/package-info.java
0 → 100644
src/main/java/cn/iocoder/dashboard/framework/security/《芋道 Spring Boot 安全框架 Spring Security 入门》.md
0 → 100644
src/main/java/cn/iocoder/dashboard/modules/system/convert/auth/SysAuthConvert.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.convert.auth; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.security.core.LoginUser; | |
| 4 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO; | |
| 5 | +import org.mapstruct.Mapper; | |
| 6 | +import org.mapstruct.Mapping; | |
| 7 | +import org.mapstruct.factory.Mappers; | |
| 8 | + | |
| 9 | +@Mapper | |
| 10 | +public interface SysAuthConvert { | |
| 11 | + | |
| 12 | + SysAuthConvert INSTANCE = Mappers.getMapper(SysAuthConvert.class); | |
| 13 | + | |
| 14 | + @Mapping(source = "updateTime", target = "updateTime", ignore = true) // 字段相同,但是含义不同,忽略 | |
| 15 | + LoginUser convert(SysUserDO bean); | |
| 16 | + | |
| 17 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/package-info.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.dal.mysql.dao; | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/user/SysUserMapper.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.dal.mysql.dao.user; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO; | |
| 4 | +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | |
| 5 | +import com.baomidou.mybatisplus.core.mapper.BaseMapper; | |
| 6 | +import org.apache.ibatis.annotations.Mapper; | |
| 7 | + | |
| 8 | +@Mapper | |
| 9 | +public interface SysUserMapper extends BaseMapper<SysUserDO> { | |
| 10 | + | |
| 11 | + default SysUserDO selectByUsername(String username) { | |
| 12 | + return selectOne(new QueryWrapper<SysUserDO>().eq("username", username)); | |
| 13 | + } | |
| 14 | + | |
| 15 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/user/SysUserDO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user; | |
| 2 | + | |
| 3 | +import java.util.Date; | |
| 4 | +import java.util.List; | |
| 5 | +import javax.validation.constraints.Email; | |
| 6 | +import javax.validation.constraints.NotBlank; | |
| 7 | +import javax.validation.constraints.Size; | |
| 8 | + | |
| 9 | +import cn.iocoder.dashboard.framework.excel.Excel; | |
| 10 | +import cn.iocoder.dashboard.framework.excel.Excels; | |
| 11 | +import cn.iocoder.dashboard.framework.mybatis.core.BaseDO; | |
| 12 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dept.SysDept; | |
| 13 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRole; | |
| 14 | +import com.baomidou.mybatisplus.annotation.TableField; | |
| 15 | +import com.baomidou.mybatisplus.annotation.TableId; | |
| 16 | +import com.baomidou.mybatisplus.annotation.TableName; | |
| 17 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
| 18 | +import com.fasterxml.jackson.annotation.JsonProperty; | |
| 19 | +import lombok.Data; | |
| 20 | +import lombok.EqualsAndHashCode; | |
| 21 | +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | |
| 22 | + | |
| 23 | +/** | |
| 24 | + * 用户 DO | |
| 25 | + * | |
| 26 | + * @author ruoyi | |
| 27 | + */ | |
| 28 | +@TableName("sys_user") | |
| 29 | +@Data | |
| 30 | +@EqualsAndHashCode(callSuper = true) | |
| 31 | +// TODO 芋艿:数据库的字段默认值 | |
| 32 | +public class SysUserDO extends BaseDO { | |
| 33 | + | |
| 34 | + /** | |
| 35 | + * 用户ID | |
| 36 | + */ | |
| 37 | + @TableId | |
| 38 | + @Excel(name = "用户序号", cellType = Excel.ColumnType.NUMERIC, prompt = "用户编号") | |
| 39 | + private Long userId; | |
| 40 | + | |
| 41 | + /** | |
| 42 | + * 部门ID | |
| 43 | + */ | |
| 44 | + @Excel(name = "部门编号", type = Excel.Type.IMPORT) | |
| 45 | + private Long deptId; | |
| 46 | + | |
| 47 | + /** | |
| 48 | + * 用户账号 | |
| 49 | + */ | |
| 50 | + @Excel(name = "登录名称") | |
| 51 | + @NotBlank(message = "用户账号不能为空") | |
| 52 | + @Size(max = 30, message = "用户账号长度不能超过30个字符") | |
| 53 | + private String username; | |
| 54 | + | |
| 55 | + /** | |
| 56 | + * 加密后的密码 | |
| 57 | + * | |
| 58 | + * 因为目前使用 {@link BCryptPasswordEncoder} 加密器,所以无需自己处理 salt 盐 | |
| 59 | + */ | |
| 60 | + private String password; | |
| 61 | + | |
| 62 | + /** | |
| 63 | + * 用户昵称 | |
| 64 | + */ | |
| 65 | + @Excel(name = "用户名称") | |
| 66 | + @Size(max = 30, message = "用户昵称长度不能超过30个字符") | |
| 67 | + private String nickname; | |
| 68 | + | |
| 69 | + /** | |
| 70 | + * 用户邮箱 | |
| 71 | + */ | |
| 72 | + @Excel(name = "用户邮箱") | |
| 73 | + @Email(message = "邮箱格式不正确") | |
| 74 | + @Size(max = 50, message = "邮箱长度不能超过50个字符") | |
| 75 | + private String email; | |
| 76 | + | |
| 77 | + /** | |
| 78 | + * 手机号码 | |
| 79 | + */ | |
| 80 | + @Excel(name = "手机号码") | |
| 81 | + @Size(max = 11, message = "手机号码长度不能超过11个字符") | |
| 82 | + private String mobile; | |
| 83 | + | |
| 84 | + /** | |
| 85 | + * 用户性别 | |
| 86 | + */ | |
| 87 | + @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知") | |
| 88 | + private String sex; | |
| 89 | + | |
| 90 | + /** | |
| 91 | + * 用户头像 | |
| 92 | + */ | |
| 93 | + private String avatar; | |
| 94 | + | |
| 95 | + /** | |
| 96 | + * 帐号状态(0正常 1停用) | |
| 97 | + */ | |
| 98 | + @Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用") | |
| 99 | + // TODO 芋艿:修改成枚举 | |
| 100 | + private String status; | |
| 101 | + | |
| 102 | + /** | |
| 103 | + * 最后登录IP | |
| 104 | + */ | |
| 105 | + @Excel(name = "最后登录IP", type = Excel.Type.EXPORT) | |
| 106 | + private String loginIp; | |
| 107 | + | |
| 108 | + /** | |
| 109 | + * 最后登录时间 | |
| 110 | + */ | |
| 111 | + @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Excel.Type.EXPORT) | |
| 112 | + private Date loginDate; | |
| 113 | + | |
| 114 | + | |
| 115 | + // TODO FROM 芋艿:下面的字段,需要忽略 | |
| 116 | + | |
| 117 | + /** | |
| 118 | + * 部门对象 | |
| 119 | + */ | |
| 120 | + @Excels({ | |
| 121 | + @Excel(name = "部门名称", targetAttr = "deptName", type = Excel.Type.EXPORT), | |
| 122 | + @Excel(name = "部门负责人", targetAttr = "leader", type = Excel.Type.EXPORT) | |
| 123 | + }) | |
| 124 | + @TableField(exist = false) | |
| 125 | + private SysDept dept; | |
| 126 | + | |
| 127 | + /** | |
| 128 | + * 角色对象 | |
| 129 | + */ | |
| 130 | + @TableField(exist = false) | |
| 131 | + private List<SysRole> roles; | |
| 132 | + | |
| 133 | + /** | |
| 134 | + * 角色组 | |
| 135 | + */ | |
| 136 | + @TableField(exist = false) | |
| 137 | + private Long[] roleIds; | |
| 138 | + | |
| 139 | + /** | |
| 140 | + * 岗位组 | |
| 141 | + */ | |
| 142 | + @TableField(exist = false) | |
| 143 | + private Long[] postIds; | |
| 144 | + | |
| 145 | + // TODO 芋艿:后续清理掉 | |
| 146 | + public boolean isAdmin() { | |
| 147 | + return isAdmin(this.userId); | |
| 148 | + } | |
| 149 | + | |
| 150 | + public static boolean isAdmin(Long userId) { | |
| 151 | + return userId != null && 1L == userId; | |
| 152 | + } | |
| 153 | + | |
| 154 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.enums; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.common.exception.ErrorCode; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * 错误码枚举类 | |
| 7 | + * | |
| 8 | + * system 系统,使用 1-002-000-000 段 | |
| 9 | + */ | |
| 10 | +public interface SysErrorCodeConstants { | |
| 11 | + | |
| 12 | + // ========== AUTH 模块 1002000000 ========== | |
| 13 | + ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1002000000, "登录失败,账号密码不正确"); | |
| 14 | + ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1002000001, "登录失败,账号被禁用"); | |
| 15 | + ErrorCode AUTH_LOGIN_FAIL_UNKNOWN = new ErrorCode(1002000002, "登录失败"); // 登陆失败的兜底,位置原因 | |
| 16 | + | |
| 17 | + // ========== TOKEN 模块 1002001000 ========== | |
| 18 | + ErrorCode TOKEN_EXPIRED = new ErrorCode(1002001000, "Token 已经过期"); | |
| 19 | + ErrorCode TOKEN_PARSE_FAIL = new ErrorCode(1002001001, "Token 解析失败"); | |
| 20 | + | |
| 21 | +} | ... | ... |
ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/enums/user/UserStatus.java
| 1 | -package com.ruoyi.common.enums; | |
| 2 | - | |
| 3 | -/** | |
| 4 | - * 用户状态 | |
| 5 | - * | |
| 6 | - * @author ruoyi | |
| 7 | - */ | |
| 8 | -public enum UserStatus | |
| 9 | -{ | |
| 10 | - OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除"); | |
| 11 | - | |
| 12 | - private final String code; | |
| 13 | - private final String info; | |
| 14 | - | |
| 15 | - UserStatus(String code, String info) | |
| 16 | - { | |
| 17 | - this.code = code; | |
| 18 | - this.info = info; | |
| 19 | - } | |
| 20 | - | |
| 21 | - public String getCode() | |
| 22 | - { | |
| 23 | - return code; | |
| 24 | - } | |
| 25 | - | |
| 26 | - public String getInfo() | |
| 27 | - { | |
| 28 | - return info; | |
| 29 | - } | |
| 30 | -} | |
| 1 | +package cn.iocoder.dashboard.modules.system.enums.user; | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * 用户状态 | |
| 5 | + * | |
| 6 | + * @author ruoyi | |
| 7 | + */ | |
| 8 | +public enum UserStatus | |
| 9 | +{ | |
| 10 | + OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除"); | |
| 11 | + | |
| 12 | + private final String code; | |
| 13 | + private final String info; | |
| 14 | + | |
| 15 | + UserStatus(String code, String info) | |
| 16 | + { | |
| 17 | + this.code = code; | |
| 18 | + this.info = info; | |
| 19 | + } | |
| 20 | + | |
| 21 | + public String getCode() | |
| 22 | + { | |
| 23 | + return code; | |
| 24 | + } | |
| 25 | + | |
| 26 | + public String getInfo() | |
| 27 | + { | |
| 28 | + return info; | |
| 29 | + } | |
| 30 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysAuthService.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.auth; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.security.core.service.SecurityFrameworkService; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * 认证 Service 接口 | |
| 7 | + * | |
| 8 | + * 提供用户的账号密码登陆、token 的校验等认证相关的功能 | |
| 9 | + * | |
| 10 | + * @author 芋道源码 | |
| 11 | + */ | |
| 12 | +public interface SysAuthService extends SecurityFrameworkService { | |
| 13 | + | |
| 14 | + String login(String username, String password, String captchaUUID, String captchaCode); | |
| 15 | + | |
| 16 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/auth/SysTokenService.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.auth; | |
| 2 | + | |
| 3 | +import io.jsonwebtoken.Claims; | |
| 4 | + | |
| 5 | +import java.util.Map; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * Token Service 接口 | |
| 9 | + * | |
| 10 | + * 提供访问 Token 令牌,目前基于 JWT 实现 | |
| 11 | + */ | |
| 12 | +public interface SysTokenService { | |
| 13 | + | |
| 14 | + /** | |
| 15 | + * 创建 Token | |
| 16 | + * | |
| 17 | + * @param subject 主体 | |
| 18 | + * @return Token 字符串 | |
| 19 | + */ | |
| 20 | + String createToken(String subject); | |
| 21 | + | |
| 22 | + /** | |
| 23 | + * 解析 Token,返回 claims 数据声明 | |
| 24 | + * | |
| 25 | + * @param token Token | |
| 26 | + * @return claims | |
| 27 | + */ | |
| 28 | + Claims parseToken(String token); | |
| 29 | + | |
| 30 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.auth.impl; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.IdUtil; | |
| 4 | +import cn.hutool.core.util.StrUtil; | |
| 5 | +import cn.iocoder.dashboard.framework.security.config.SecurityProperties; | |
| 6 | +import cn.iocoder.dashboard.framework.security.core.LoginUser; | |
| 7 | +import cn.iocoder.dashboard.modules.system.enums.user.UserStatus; | |
| 8 | +import cn.iocoder.dashboard.modules.system.convert.auth.SysAuthConvert; | |
| 9 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO; | |
| 10 | +import cn.iocoder.dashboard.modules.system.dal.redis.dao.auth.SysLoginUserRedisDAO; | |
| 11 | +import cn.iocoder.dashboard.modules.system.service.auth.SysAuthService; | |
| 12 | +import cn.iocoder.dashboard.modules.system.service.auth.SysTokenService; | |
| 13 | +import cn.iocoder.dashboard.modules.system.service.user.SysUserService; | |
| 14 | +import cn.iocoder.dashboard.util.date.DateUtils; | |
| 15 | +import io.jsonwebtoken.Claims; | |
| 16 | +import io.jsonwebtoken.JwtException; | |
| 17 | +import lombok.extern.slf4j.Slf4j; | |
| 18 | +import org.springframework.security.authentication.AuthenticationManager; | |
| 19 | +import org.springframework.security.authentication.BadCredentialsException; | |
| 20 | +import org.springframework.security.authentication.DisabledException; | |
| 21 | +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | |
| 22 | +import org.springframework.security.core.Authentication; | |
| 23 | +import org.springframework.security.core.AuthenticationException; | |
| 24 | +import org.springframework.security.core.userdetails.UserDetails; | |
| 25 | +import org.springframework.security.core.userdetails.UsernameNotFoundException; | |
| 26 | +import org.springframework.stereotype.Service; | |
| 27 | +import org.springframework.util.Assert; | |
| 28 | + | |
| 29 | +import javax.annotation.Resource; | |
| 30 | +import java.util.Collections; | |
| 31 | +import java.util.Date; | |
| 32 | +import java.util.Set; | |
| 33 | + | |
| 34 | +import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception; | |
| 35 | +import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; | |
| 36 | + | |
| 37 | +/** | |
| 38 | + * Auth Service 实现类 | |
| 39 | + * | |
| 40 | + * @author 芋道源码 | |
| 41 | + */ | |
| 42 | +@Service | |
| 43 | +@Slf4j | |
| 44 | +public class SysAuthServiceImpl implements SysAuthService { | |
| 45 | + | |
| 46 | + @Resource | |
| 47 | + private SecurityProperties securityProperties; | |
| 48 | + | |
| 49 | + @Resource | |
| 50 | + private SysTokenService tokenService; | |
| 51 | + @Resource | |
| 52 | + private AuthenticationManager authenticationManager; | |
| 53 | + @Resource | |
| 54 | + private SysUserService userService; | |
| 55 | + @Resource | |
| 56 | + private SysLoginUserRedisDAO loginUserRedisDAO; | |
| 57 | + | |
| 58 | + @Override | |
| 59 | + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { | |
| 60 | + // 获取 username 对应的 SysUserDO | |
| 61 | + SysUserDO user = userService.getUserByUserName(username); | |
| 62 | + if (user == null) { | |
| 63 | + throw new UsernameNotFoundException(username); | |
| 64 | + } | |
| 65 | + | |
| 66 | + // 创建 LoginUser 对象 | |
| 67 | + return SysAuthConvert.INSTANCE.convert(user); | |
| 68 | + } | |
| 69 | + | |
| 70 | + @Override | |
| 71 | + public LoginUser mockLogin(Long userId) { | |
| 72 | + // 获取用户编号对应的 SysUserDO | |
| 73 | + SysUserDO user = userService.getUser(userId); | |
| 74 | + if (user == null) { | |
| 75 | + throw new UsernameNotFoundException(String.valueOf(userId)); | |
| 76 | + } | |
| 77 | + | |
| 78 | + // 创建 LoginUser 对象 | |
| 79 | + LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user); | |
| 80 | + loginUser.setUpdateTime(new Date()); | |
| 81 | + loginUser.setRoleIds(this.getUserRoleIds(loginUser.getUserId())); | |
| 82 | + return loginUser; | |
| 83 | + } | |
| 84 | + | |
| 85 | + @Override | |
| 86 | + public String login(String username, String password, String captchaUUID, String captchaCode) { | |
| 87 | + // 判断验证码是否正确 | |
| 88 | + this.verifyCaptcha(captchaUUID, captchaCode); | |
| 89 | + | |
| 90 | + // 使用账号密码,进行登陆。 | |
| 91 | + LoginUser loginUser = this.login0(username, password); | |
| 92 | + | |
| 93 | + // 缓存登陆用户到 Redis 中 | |
| 94 | + String sessionId = IdUtil.fastSimpleUUID(); | |
| 95 | + loginUser.setUpdateTime(new Date()); | |
| 96 | + loginUser.setRoleIds(this.getUserRoleIds(loginUser.getUserId())); | |
| 97 | + loginUserRedisDAO.set(sessionId, loginUser); | |
| 98 | + | |
| 99 | + // 创建 Token | |
| 100 | + // 我们在返回给前端的 JWT 中,使用 sessionId 作为 subject 主体,标识当前 User 用户 | |
| 101 | + return tokenService.createToken(sessionId); | |
| 102 | + } | |
| 103 | + | |
| 104 | + private void verifyCaptcha(String captchaUUID, String captchaCode) { | |
| 105 | + // String verifyKey = Constants.CAPTCHA_CODE_KEY + captchaUUID; | |
| 106 | +// String captcha = redisCache.getCacheObject(verifyKey); | |
| 107 | +// redisCache.deleteObject(verifyKey); | |
| 108 | +// if (captcha == null) { | |
| 109 | +// AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"))); | |
| 110 | +// throw new CaptchaExpireException(); | |
| 111 | +// } | |
| 112 | +// if (!code.equalsIgnoreCase(captcha)) { | |
| 113 | +// AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"))); | |
| 114 | +// throw new CaptchaException(); | |
| 115 | +// } | |
| 116 | + } | |
| 117 | + | |
| 118 | + private LoginUser login0(String username, String password) { | |
| 119 | + // 用户验证 | |
| 120 | + Authentication authentication; | |
| 121 | + try { | |
| 122 | + // 调用 Spring Security 的 AuthenticationManager#authenticate(...) 方法,使用账号密码进行认证 | |
| 123 | + // 在其内部,会调用到 loadUserByUsername 方法,获取 User 信息 | |
| 124 | + authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password)); | |
| 125 | + } catch (BadCredentialsException badCredentialsException) { | |
| 126 | + // TODO 日志优化 | |
| 127 | +// AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); | |
| 128 | + throw exception(AUTH_LOGIN_BAD_CREDENTIALS); | |
| 129 | + } catch (DisabledException disabledException) { | |
| 130 | + // TODO 日志优化 | |
| 131 | + throw exception(AUTH_LOGIN_USER_DISABLED); | |
| 132 | + } catch (AuthenticationException authenticationException) { | |
| 133 | + // TODO 日志优化 | |
| 134 | + throw exception(AUTH_LOGIN_FAIL_UNKNOWN); | |
| 135 | + } | |
| 136 | + // TODO 需要优化 | |
| 137 | +// AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); | |
| 138 | + Assert.notNull(authentication.getPrincipal(), "Principal 不会为空"); | |
| 139 | + return (LoginUser) authentication.getPrincipal(); | |
| 140 | + } | |
| 141 | + | |
| 142 | + /** | |
| 143 | + * 获得 User 拥有的角色编号数组 | |
| 144 | + * | |
| 145 | + * @param userId 用户编号 | |
| 146 | + * @return 角色编号数组 | |
| 147 | + */ | |
| 148 | + private Set<Integer> getUserRoleIds(Long userId) { | |
| 149 | + // TODO 芋艿:读取角色编号 | |
| 150 | + return Collections.emptySet(); | |
| 151 | + } | |
| 152 | + | |
| 153 | + @Override | |
| 154 | + public void logout(String token) { | |
| 155 | +// AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功")); TODO 需要搞一搞 | |
| 156 | + } | |
| 157 | + | |
| 158 | + @Override | |
| 159 | + public LoginUser verifyTokenAndRefresh(String token) { | |
| 160 | + // 验证 token 的有效性 | |
| 161 | + String sessionId = this.verifyToken(token); | |
| 162 | + // 获得 LoginUser | |
| 163 | + LoginUser loginUser = loginUserRedisDAO.get(sessionId); | |
| 164 | + if (loginUser == null) { | |
| 165 | +// throw exception(AUTH_SESSION_TIMEOUT); | |
| 166 | + return null; | |
| 167 | + } | |
| 168 | + // 刷新 LoginUser 缓存 | |
| 169 | + this.refreshLoginUserCache(sessionId, loginUser); | |
| 170 | + return loginUser; | |
| 171 | + } | |
| 172 | + | |
| 173 | + private String verifyToken(String token) { | |
| 174 | + Claims claims; | |
| 175 | + try { | |
| 176 | + claims = tokenService.parseToken(token); | |
| 177 | + } catch (JwtException jwtException) { | |
| 178 | + log.warn("[verifyToken][token({}) 解析发生异常]", token); | |
| 179 | +// throw exception(TOKEN_PARSE_FAIL); | |
| 180 | + return null; | |
| 181 | + } | |
| 182 | + // token 已经过期 | |
| 183 | + if (DateUtils.isExpired(claims.getExpiration())) { | |
| 184 | +// throw exception(TOKEN_EXPIRED); | |
| 185 | + return null; | |
| 186 | + } | |
| 187 | + // 判断 sessionId 是否存在 | |
| 188 | + String sessionId = claims.getSubject(); | |
| 189 | + if (StrUtil.isBlank(sessionId)) { | |
| 190 | +// throw exception(AUTH_SESSION_ID_NOT_FOUND); | |
| 191 | + return null; | |
| 192 | + } | |
| 193 | + return sessionId; | |
| 194 | + } | |
| 195 | + | |
| 196 | + private void refreshLoginUserCache(String sessionId, LoginUser loginUser) { | |
| 197 | + // 每 1/3 的 Session 超时时间,刷新 LoginUser 缓存 | |
| 198 | + if (System.currentTimeMillis() - loginUser.getUpdateTime().getTime() < | |
| 199 | + securityProperties.getSessionTimeout().toMillis() / 3) { | |
| 200 | + return; | |
| 201 | + } | |
| 202 | + | |
| 203 | + // 重新加载 SysUserDO 信息 | |
| 204 | + SysUserDO user = userService.getUser(loginUser.getUserId()); | |
| 205 | + if (user == null || UserStatus.DISABLE.getCode().equals(user.getStatus())) { | |
| 206 | + throw exception(TOKEN_EXPIRED); // 校验 token 时,用户被禁用的情况下,也认为 token 过期,方便前端跳转到登陆界面 | |
| 207 | + } | |
| 208 | + | |
| 209 | + // 刷新 LoginUser 缓存 | |
| 210 | + loginUser.setUpdateTime(new Date()); | |
| 211 | + loginUser.setRoleIds(this.getUserRoleIds(loginUser.getUserId())); | |
| 212 | + loginUserRedisDAO.set(sessionId, loginUser); | |
| 213 | + } | |
| 214 | + | |
| 215 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysTokenServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.auth.impl; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.security.config.SecurityProperties; | |
| 4 | +import cn.iocoder.dashboard.modules.system.service.auth.SysTokenService; | |
| 5 | +import cn.iocoder.dashboard.util.date.DateUtils; | |
| 6 | +import io.jsonwebtoken.Claims; | |
| 7 | +import io.jsonwebtoken.Jwts; | |
| 8 | +import io.jsonwebtoken.SignatureAlgorithm; | |
| 9 | +import org.springframework.stereotype.Service; | |
| 10 | + | |
| 11 | +import javax.annotation.Resource; | |
| 12 | +import java.util.HashMap; | |
| 13 | +import java.util.Map; | |
| 14 | + | |
| 15 | +/** | |
| 16 | + * Token Service 实现类 | |
| 17 | + * | |
| 18 | + * @author 芋道源码 | |
| 19 | + */ | |
| 20 | +@Service | |
| 21 | +public class SysTokenServiceImpl implements SysTokenService { | |
| 22 | + | |
| 23 | + @Resource | |
| 24 | + private SecurityProperties securityProperties; | |
| 25 | + | |
| 26 | + @Override | |
| 27 | + public String createToken(String subject) { | |
| 28 | + return Jwts.builder() | |
| 29 | + .signWith(SignatureAlgorithm.HS512, securityProperties.getTokenSecret()) | |
| 30 | + .setExpiration(DateUtils.addTime(securityProperties.getTokenTimeout())) | |
| 31 | + .setSubject(subject) | |
| 32 | + .compact(); | |
| 33 | + } | |
| 34 | + | |
| 35 | + @Override | |
| 36 | + public Claims parseToken(String token) { | |
| 37 | + return Jwts.parser() | |
| 38 | + .setSigningKey(securityProperties.getTokenSecret()) | |
| 39 | + .parseClaimsJws(token) | |
| 40 | + .getBody(); | |
| 41 | + } | |
| 42 | + | |
| 43 | + public static void main(String[] args) { | |
| 44 | + String secret = "abcdefghijklmnopqrstuvwxyz"; | |
| 45 | + Map<String, Object> map = new HashMap<>(); | |
| 46 | + map.put("key1", "value1"); | |
| 47 | + System.out.println(Jwts.builder() | |
| 48 | + .signWith(SignatureAlgorithm.HS512, secret) | |
| 49 | + .setClaims(map) | |
| 50 | + .compact()); | |
| 51 | + | |
| 52 | + System.out.println(Jwts.parser() | |
| 53 | + .setSigningKey(secret) | |
| 54 | + .parseClaimsJws("qyJhbGciOiJIUzUxMiJ9.eyJrZXkxIjoidmFsdWUxIn0.AHWncLRBlJkqrKaoWHZmMgbqYIT7rfLs8KCp9LuC0mdNfnx1xEMm1N9bgcD-0lc5sjySqsKiWzqJ3rpoyUSh0g") | |
| 55 | + .getBody()); | |
| 56 | + } | |
| 57 | + | |
| 58 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserService.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.user; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * 用户 Service 接口 | |
| 7 | + * | |
| 8 | + * @author ruoyi | |
| 9 | + */ | |
| 10 | +public interface SysUserService { | |
| 11 | +// /** | |
| 12 | +// * 根据条件分页查询用户列表 | |
| 13 | +// * | |
| 14 | +// * @param user 用户信息 | |
| 15 | +// * @return 用户信息集合信息 | |
| 16 | +// */ | |
| 17 | +// public List<SysUser> selectUserList(SysUser user); | |
| 18 | + | |
| 19 | + /** | |
| 20 | + * 通过用户名查询用户 | |
| 21 | + * | |
| 22 | + * @param username 用户名 | |
| 23 | + * @return 用户对象信息 | |
| 24 | + */ | |
| 25 | + SysUserDO getUserByUserName(String username); | |
| 26 | + | |
| 27 | + /** | |
| 28 | + * 通过用户 ID 查询用户 | |
| 29 | + * | |
| 30 | + * @param userId 用户ID | |
| 31 | + * @return 用户对象信息 | |
| 32 | + */ | |
| 33 | + SysUserDO getUser(Long userId); | |
| 34 | + | |
| 35 | +// | |
| 36 | +// /** | |
| 37 | +// * 根据用户ID查询用户所属角色组 | |
| 38 | +// * | |
| 39 | +// * @param userName 用户名 | |
| 40 | +// * @return 结果 | |
| 41 | +// */ | |
| 42 | +// public String selectUserRoleGroup(String userName); | |
| 43 | +// | |
| 44 | +// /** | |
| 45 | +// * 根据用户ID查询用户所属岗位组 | |
| 46 | +// * | |
| 47 | +// * @param userName 用户名 | |
| 48 | +// * @return 结果 | |
| 49 | +// */ | |
| 50 | +// public String selectUserPostGroup(String userName); | |
| 51 | +// | |
| 52 | +// /** | |
| 53 | +// * 校验用户名称是否唯一 | |
| 54 | +// * | |
| 55 | +// * @param userName 用户名称 | |
| 56 | +// * @return 结果 | |
| 57 | +// */ | |
| 58 | +// public String checkUserNameUnique(String userName); | |
| 59 | +// | |
| 60 | +// /** | |
| 61 | +// * 校验手机号码是否唯一 | |
| 62 | +// * | |
| 63 | +// * @param user 用户信息 | |
| 64 | +// * @return 结果 | |
| 65 | +// */ | |
| 66 | +// public String checkPhoneUnique(SysUser user); | |
| 67 | +// | |
| 68 | +// /** | |
| 69 | +// * 校验email是否唯一 | |
| 70 | +// * | |
| 71 | +// * @param user 用户信息 | |
| 72 | +// * @return 结果 | |
| 73 | +// */ | |
| 74 | +// public String checkEmailUnique(SysUser user); | |
| 75 | +// | |
| 76 | +// /** | |
| 77 | +// * 校验用户是否允许操作 | |
| 78 | +// * | |
| 79 | +// * @param user 用户信息 | |
| 80 | +// */ | |
| 81 | +// public void checkUserAllowed(SysUser user); | |
| 82 | +// | |
| 83 | +// /** | |
| 84 | +// * 新增用户信息 | |
| 85 | +// * | |
| 86 | +// * @param user 用户信息 | |
| 87 | +// * @return 结果 | |
| 88 | +// */ | |
| 89 | +// public int insertUser(SysUser user); | |
| 90 | +// | |
| 91 | +// /** | |
| 92 | +// * 修改用户信息 | |
| 93 | +// * | |
| 94 | +// * @param user 用户信息 | |
| 95 | +// * @return 结果 | |
| 96 | +// */ | |
| 97 | +// public int updateUser(SysUser user); | |
| 98 | +// | |
| 99 | +// /** | |
| 100 | +// * 修改用户状态 | |
| 101 | +// * | |
| 102 | +// * @param user 用户信息 | |
| 103 | +// * @return 结果 | |
| 104 | +// */ | |
| 105 | +// public int updateUserStatus(SysUser user); | |
| 106 | +// | |
| 107 | +// /** | |
| 108 | +// * 修改用户基本信息 | |
| 109 | +// * | |
| 110 | +// * @param user 用户信息 | |
| 111 | +// * @return 结果 | |
| 112 | +// */ | |
| 113 | +// public int updateUserProfile(SysUser user); | |
| 114 | +// | |
| 115 | +// /** | |
| 116 | +// * 修改用户头像 | |
| 117 | +// * | |
| 118 | +// * @param userName 用户名 | |
| 119 | +// * @param avatar 头像地址 | |
| 120 | +// * @return 结果 | |
| 121 | +// */ | |
| 122 | +// public boolean updateUserAvatar(String userName, String avatar); | |
| 123 | +// | |
| 124 | +// /** | |
| 125 | +// * 重置用户密码 | |
| 126 | +// * | |
| 127 | +// * @param user 用户信息 | |
| 128 | +// * @return 结果 | |
| 129 | +// */ | |
| 130 | +// public int resetPwd(SysUser user); | |
| 131 | +// | |
| 132 | +// /** | |
| 133 | +// * 重置用户密码 | |
| 134 | +// * | |
| 135 | +// * @param userName 用户名 | |
| 136 | +// * @param password 密码 | |
| 137 | +// * @return 结果 | |
| 138 | +// */ | |
| 139 | +// public int resetUserPwd(String userName, String password); | |
| 140 | +// | |
| 141 | +// /** | |
| 142 | +// * 通过用户ID删除用户 | |
| 143 | +// * | |
| 144 | +// * @param userId 用户ID | |
| 145 | +// * @return 结果 | |
| 146 | +// */ | |
| 147 | +// public int deleteUserById(Long userId); | |
| 148 | +// | |
| 149 | +// /** | |
| 150 | +// * 批量删除用户信息 | |
| 151 | +// * | |
| 152 | +// * @param userIds 需要删除的用户ID | |
| 153 | +// * @return 结果 | |
| 154 | +// */ | |
| 155 | +// public int deleteUserByIds(Long[] userIds); | |
| 156 | +// | |
| 157 | +// /** | |
| 158 | +// * 导入用户数据 | |
| 159 | +// * | |
| 160 | +// * @param userList 用户数据列表 | |
| 161 | +// * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 | |
| 162 | +// * @param operName 操作用户 | |
| 163 | +// * @return 结果 | |
| 164 | +// */ | |
| 165 | +// public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName); | |
| 166 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/user/SysUserServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.user; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dao.user.SysUserMapper; | |
| 4 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.user.SysUserDO; | |
| 5 | +import lombok.extern.slf4j.Slf4j; | |
| 6 | +import org.springframework.stereotype.Service; | |
| 7 | + | |
| 8 | +import javax.annotation.Resource; | |
| 9 | + | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * 用户 业务层处理 | |
| 13 | + * | |
| 14 | + * @author ruoyi | |
| 15 | + */ | |
| 16 | +@Service | |
| 17 | +@Slf4j | |
| 18 | +public class SysUserServiceImpl implements SysUserService { | |
| 19 | + | |
| 20 | + @Resource | |
| 21 | + private SysUserMapper userMapper; | |
| 22 | + | |
| 23 | +// @Autowired | |
| 24 | +// private SysUserMapper userMapper; | |
| 25 | +// | |
| 26 | +// @Autowired | |
| 27 | +// private SysRoleMapper roleMapper; | |
| 28 | +// | |
| 29 | +// @Autowired | |
| 30 | +// private SysPostMapper postMapper; | |
| 31 | +// | |
| 32 | +// @Autowired | |
| 33 | +// private SysUserRoleMapper userRoleMapper; | |
| 34 | +// | |
| 35 | +// @Autowired | |
| 36 | +// private SysUserPostMapper userPostMapper; | |
| 37 | +// | |
| 38 | +// @Autowired | |
| 39 | +// private ISysConfigService configService; | |
| 40 | + | |
| 41 | +// /** | |
| 42 | +// * 根据条件分页查询用户列表 | |
| 43 | +// * | |
| 44 | +// * @param user 用户信息 | |
| 45 | +// * @return 用户信息集合信息 | |
| 46 | +// */ | |
| 47 | +// @Override | |
| 48 | +// @DataScope(deptAlias = "d", userAlias = "u") | |
| 49 | +// public List<SysUser> selectUserList(SysUser user) | |
| 50 | +// { | |
| 51 | +// return userMapper.selectUserList(user); | |
| 52 | +// } | |
| 53 | + | |
| 54 | + @Override | |
| 55 | + public SysUserDO getUserByUserName(String username) { | |
| 56 | + return userMapper.selectByUsername(username); | |
| 57 | + } | |
| 58 | + | |
| 59 | + @Override | |
| 60 | + public SysUserDO getUser(Long userId) { | |
| 61 | + return userMapper.selectById(userId); | |
| 62 | + } | |
| 63 | + | |
| 64 | +// /** | |
| 65 | +// * 通过用户ID查询用户 | |
| 66 | +// * | |
| 67 | +// * @param userId 用户ID | |
| 68 | +// * @return 用户对象信息 | |
| 69 | +// */ | |
| 70 | +// @Override | |
| 71 | +// public SysUser selectUserById(Long userId) | |
| 72 | +// { | |
| 73 | +// return userMapper.selectUserById(userId); | |
| 74 | +// } | |
| 75 | +// | |
| 76 | +// /** | |
| 77 | +// * 查询用户所属角色组 | |
| 78 | +// * | |
| 79 | +// * @param userName 用户名 | |
| 80 | +// * @return 结果 | |
| 81 | +// */ | |
| 82 | +// @Override | |
| 83 | +// public String selectUserRoleGroup(String userName) | |
| 84 | +// { | |
| 85 | +// List<SysRole> list = roleMapper.selectRolesByUserName(userName); | |
| 86 | +// StringBuffer idsStr = new StringBuffer(); | |
| 87 | +// for (SysRole role : list) | |
| 88 | +// { | |
| 89 | +// idsStr.append(role.getRoleName()).append(","); | |
| 90 | +// } | |
| 91 | +// if (StringUtils.isNotEmpty(idsStr.toString())) | |
| 92 | +// { | |
| 93 | +// return idsStr.substring(0, idsStr.length() - 1); | |
| 94 | +// } | |
| 95 | +// return idsStr.toString(); | |
| 96 | +// } | |
| 97 | +// | |
| 98 | +// /** | |
| 99 | +// * 查询用户所属岗位组 | |
| 100 | +// * | |
| 101 | +// * @param userName 用户名 | |
| 102 | +// * @return 结果 | |
| 103 | +// */ | |
| 104 | +// @Override | |
| 105 | +// public String selectUserPostGroup(String userName) | |
| 106 | +// { | |
| 107 | +// List<SysPost> list = postMapper.selectPostsByUserName(userName); | |
| 108 | +// StringBuffer idsStr = new StringBuffer(); | |
| 109 | +// for (SysPost post : list) | |
| 110 | +// { | |
| 111 | +// idsStr.append(post.getPostName()).append(","); | |
| 112 | +// } | |
| 113 | +// if (StringUtils.isNotEmpty(idsStr.toString())) | |
| 114 | +// { | |
| 115 | +// return idsStr.substring(0, idsStr.length() - 1); | |
| 116 | +// } | |
| 117 | +// return idsStr.toString(); | |
| 118 | +// } | |
| 119 | +// | |
| 120 | +// /** | |
| 121 | +// * 校验用户名称是否唯一 | |
| 122 | +// * | |
| 123 | +// * @param userName 用户名称 | |
| 124 | +// * @return 结果 | |
| 125 | +// */ | |
| 126 | +// @Override | |
| 127 | +// public String checkUserNameUnique(String userName) | |
| 128 | +// { | |
| 129 | +// int count = userMapper.checkUserNameUnique(userName); | |
| 130 | +// if (count > 0) | |
| 131 | +// { | |
| 132 | +// return UserConstants.NOT_UNIQUE; | |
| 133 | +// } | |
| 134 | +// return UserConstants.UNIQUE; | |
| 135 | +// } | |
| 136 | +// | |
| 137 | +// /** | |
| 138 | +// * 校验用户名称是否唯一 | |
| 139 | +// * | |
| 140 | +// * @param user 用户信息 | |
| 141 | +// * @return | |
| 142 | +// */ | |
| 143 | +// @Override | |
| 144 | +// public String checkPhoneUnique(SysUser user) | |
| 145 | +// { | |
| 146 | +// Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); | |
| 147 | +// SysUser info = userMapper.checkPhoneUnique(user.getPhonenumber()); | |
| 148 | +// if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) | |
| 149 | +// { | |
| 150 | +// return UserConstants.NOT_UNIQUE; | |
| 151 | +// } | |
| 152 | +// return UserConstants.UNIQUE; | |
| 153 | +// } | |
| 154 | +// | |
| 155 | +// /** | |
| 156 | +// * 校验email是否唯一 | |
| 157 | +// * | |
| 158 | +// * @param user 用户信息 | |
| 159 | +// * @return | |
| 160 | +// */ | |
| 161 | +// @Override | |
| 162 | +// public String checkEmailUnique(SysUser user) | |
| 163 | +// { | |
| 164 | +// Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); | |
| 165 | +// SysUser info = userMapper.checkEmailUnique(user.getEmail()); | |
| 166 | +// if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) | |
| 167 | +// { | |
| 168 | +// return UserConstants.NOT_UNIQUE; | |
| 169 | +// } | |
| 170 | +// return UserConstants.UNIQUE; | |
| 171 | +// } | |
| 172 | +// | |
| 173 | +// /** | |
| 174 | +// * 校验用户是否允许操作 | |
| 175 | +// * | |
| 176 | +// * @param user 用户信息 | |
| 177 | +// */ | |
| 178 | +// @Override | |
| 179 | +// public void checkUserAllowed(SysUser user) | |
| 180 | +// { | |
| 181 | +// if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin()) | |
| 182 | +// { | |
| 183 | +// throw new CustomException("不允许操作超级管理员用户"); | |
| 184 | +// } | |
| 185 | +// } | |
| 186 | +// | |
| 187 | +// /** | |
| 188 | +// * 新增保存用户信息 | |
| 189 | +// * | |
| 190 | +// * @param user 用户信息 | |
| 191 | +// * @return 结果 | |
| 192 | +// */ | |
| 193 | +// @Override | |
| 194 | +// @Transactional | |
| 195 | +// public int insertUser(SysUser user) | |
| 196 | +// { | |
| 197 | +// // 新增用户信息 | |
| 198 | +// int rows = userMapper.insertUser(user); | |
| 199 | +// // 新增用户岗位关联 | |
| 200 | +// insertUserPost(user); | |
| 201 | +// // 新增用户与角色管理 | |
| 202 | +// insertUserRole(user); | |
| 203 | +// return rows; | |
| 204 | +// } | |
| 205 | +// | |
| 206 | +// /** | |
| 207 | +// * 修改保存用户信息 | |
| 208 | +// * | |
| 209 | +// * @param user 用户信息 | |
| 210 | +// * @return 结果 | |
| 211 | +// */ | |
| 212 | +// @Override | |
| 213 | +// @Transactional | |
| 214 | +// public int updateUser(SysUser user) | |
| 215 | +// { | |
| 216 | +// Long userId = user.getUserId(); | |
| 217 | +// // 删除用户与角色关联 | |
| 218 | +// userRoleMapper.deleteUserRoleByUserId(userId); | |
| 219 | +// // 新增用户与角色管理 | |
| 220 | +// insertUserRole(user); | |
| 221 | +// // 删除用户与岗位关联 | |
| 222 | +// userPostMapper.deleteUserPostByUserId(userId); | |
| 223 | +// // 新增用户与岗位管理 | |
| 224 | +// insertUserPost(user); | |
| 225 | +// return userMapper.updateUser(user); | |
| 226 | +// } | |
| 227 | +// | |
| 228 | +// /** | |
| 229 | +// * 修改用户状态 | |
| 230 | +// * | |
| 231 | +// * @param user 用户信息 | |
| 232 | +// * @return 结果 | |
| 233 | +// */ | |
| 234 | +// @Override | |
| 235 | +// public int updateUserStatus(SysUser user) | |
| 236 | +// { | |
| 237 | +// return userMapper.updateUser(user); | |
| 238 | +// } | |
| 239 | +// | |
| 240 | +// /** | |
| 241 | +// * 修改用户基本信息 | |
| 242 | +// * | |
| 243 | +// * @param user 用户信息 | |
| 244 | +// * @return 结果 | |
| 245 | +// */ | |
| 246 | +// @Override | |
| 247 | +// public int updateUserProfile(SysUser user) | |
| 248 | +// { | |
| 249 | +// return userMapper.updateUser(user); | |
| 250 | +// } | |
| 251 | +// | |
| 252 | +// /** | |
| 253 | +// * 修改用户头像 | |
| 254 | +// * | |
| 255 | +// * @param userName 用户名 | |
| 256 | +// * @param avatar 头像地址 | |
| 257 | +// * @return 结果 | |
| 258 | +// */ | |
| 259 | +// @Override | |
| 260 | +// public boolean updateUserAvatar(String userName, String avatar) | |
| 261 | +// { | |
| 262 | +// return userMapper.updateUserAvatar(userName, avatar) > 0; | |
| 263 | +// } | |
| 264 | +// | |
| 265 | +// /** | |
| 266 | +// * 重置用户密码 | |
| 267 | +// * | |
| 268 | +// * @param user 用户信息 | |
| 269 | +// * @return 结果 | |
| 270 | +// */ | |
| 271 | +// @Override | |
| 272 | +// public int resetPwd(SysUser user) | |
| 273 | +// { | |
| 274 | +// return userMapper.updateUser(user); | |
| 275 | +// } | |
| 276 | +// | |
| 277 | +// /** | |
| 278 | +// * 重置用户密码 | |
| 279 | +// * | |
| 280 | +// * @param userName 用户名 | |
| 281 | +// * @param password 密码 | |
| 282 | +// * @return 结果 | |
| 283 | +// */ | |
| 284 | +// @Override | |
| 285 | +// public int resetUserPwd(String userName, String password) | |
| 286 | +// { | |
| 287 | +// return userMapper.resetUserPwd(userName, password); | |
| 288 | +// } | |
| 289 | +// | |
| 290 | +// /** | |
| 291 | +// * 新增用户角色信息 | |
| 292 | +// * | |
| 293 | +// * @param user 用户对象 | |
| 294 | +// */ | |
| 295 | +// public void insertUserRole(SysUser user) | |
| 296 | +// { | |
| 297 | +// Long[] roles = user.getRoleIds(); | |
| 298 | +// if (StringUtils.isNotNull(roles)) | |
| 299 | +// { | |
| 300 | +// // 新增用户与角色管理 | |
| 301 | +// List<SysUserRole> list = new ArrayList<SysUserRole>(); | |
| 302 | +// for (Long roleId : roles) | |
| 303 | +// { | |
| 304 | +// SysUserRole ur = new SysUserRole(); | |
| 305 | +// ur.setUserId(user.getUserId()); | |
| 306 | +// ur.setRoleId(roleId); | |
| 307 | +// list.add(ur); | |
| 308 | +// } | |
| 309 | +// if (list.size() > 0) | |
| 310 | +// { | |
| 311 | +// userRoleMapper.batchUserRole(list); | |
| 312 | +// } | |
| 313 | +// } | |
| 314 | +// } | |
| 315 | +// | |
| 316 | +// /** | |
| 317 | +// * 新增用户岗位信息 | |
| 318 | +// * | |
| 319 | +// * @param user 用户对象 | |
| 320 | +// */ | |
| 321 | +// public void insertUserPost(SysUser user) | |
| 322 | +// { | |
| 323 | +// Long[] posts = user.getPostIds(); | |
| 324 | +// if (StringUtils.isNotNull(posts)) | |
| 325 | +// { | |
| 326 | +// // 新增用户与岗位管理 | |
| 327 | +// List<SysUserPost> list = new ArrayList<SysUserPost>(); | |
| 328 | +// for (Long postId : posts) | |
| 329 | +// { | |
| 330 | +// SysUserPost up = new SysUserPost(); | |
| 331 | +// up.setUserId(user.getUserId()); | |
| 332 | +// up.setPostId(postId); | |
| 333 | +// list.add(up); | |
| 334 | +// } | |
| 335 | +// if (list.size() > 0) | |
| 336 | +// { | |
| 337 | +// userPostMapper.batchUserPost(list); | |
| 338 | +// } | |
| 339 | +// } | |
| 340 | +// } | |
| 341 | +// | |
| 342 | +// /** | |
| 343 | +// * 通过用户ID删除用户 | |
| 344 | +// * | |
| 345 | +// * @param userId 用户ID | |
| 346 | +// * @return 结果 | |
| 347 | +// */ | |
| 348 | +// @Override | |
| 349 | +// @Transactional | |
| 350 | +// public int deleteUserById(Long userId) | |
| 351 | +// { | |
| 352 | +// // 删除用户与角色关联 | |
| 353 | +// userRoleMapper.deleteUserRoleByUserId(userId); | |
| 354 | +// // 删除用户与岗位表 | |
| 355 | +// userPostMapper.deleteUserPostByUserId(userId); | |
| 356 | +// return userMapper.deleteUserById(userId); | |
| 357 | +// } | |
| 358 | +// | |
| 359 | +// /** | |
| 360 | +// * 批量删除用户信息 | |
| 361 | +// * | |
| 362 | +// * @param userIds 需要删除的用户ID | |
| 363 | +// * @return 结果 | |
| 364 | +// */ | |
| 365 | +// @Override | |
| 366 | +// @Transactional | |
| 367 | +// public int deleteUserByIds(Long[] userIds) | |
| 368 | +// { | |
| 369 | +// for (Long userId : userIds) | |
| 370 | +// { | |
| 371 | +// checkUserAllowed(new SysUser(userId)); | |
| 372 | +// } | |
| 373 | +// // 删除用户与角色关联 | |
| 374 | +// userRoleMapper.deleteUserRole(userIds); | |
| 375 | +// // 删除用户与岗位关联 | |
| 376 | +// userPostMapper.deleteUserPost(userIds); | |
| 377 | +// return userMapper.deleteUserByIds(userIds); | |
| 378 | +// } | |
| 379 | +// | |
| 380 | +// /** | |
| 381 | +// * 导入用户数据 | |
| 382 | +// * | |
| 383 | +// * @param userList 用户数据列表 | |
| 384 | +// * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 | |
| 385 | +// * @param operName 操作用户 | |
| 386 | +// * @return 结果 | |
| 387 | +// */ | |
| 388 | +// @Override | |
| 389 | +// public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName) | |
| 390 | +// { | |
| 391 | +// if (StringUtils.isNull(userList) || userList.size() == 0) | |
| 392 | +// { | |
| 393 | +// throw new CustomException("导入用户数据不能为空!"); | |
| 394 | +// } | |
| 395 | +// int successNum = 0; | |
| 396 | +// int failureNum = 0; | |
| 397 | +// StringBuilder successMsg = new StringBuilder(); | |
| 398 | +// StringBuilder failureMsg = new StringBuilder(); | |
| 399 | +// String password = configService.selectConfigByKey("sys.user.initPassword"); | |
| 400 | +// for (SysUser user : userList) | |
| 401 | +// { | |
| 402 | +// try | |
| 403 | +// { | |
| 404 | +// // 验证是否存在这个用户 | |
| 405 | +// SysUser u = userMapper.selectUserByUserName(user.getUserName()); | |
| 406 | +// if (StringUtils.isNull(u)) | |
| 407 | +// { | |
| 408 | +// user.setPassword(SecurityUtils.encryptPassword(password)); | |
| 409 | +// user.setCreateBy(operName); | |
| 410 | +// this.insertUser(user); | |
| 411 | +// successNum++; | |
| 412 | +// successMsg.append("<br/>" + successNum + "、账号 " + user.getUserName() + " 导入成功"); | |
| 413 | +// } | |
| 414 | +// else if (isUpdateSupport) | |
| 415 | +// { | |
| 416 | +// user.setUpdateBy(operName); | |
| 417 | +// this.updateUser(user); | |
| 418 | +// successNum++; | |
| 419 | +// successMsg.append("<br/>" + successNum + "、账号 " + user.getUserName() + " 更新成功"); | |
| 420 | +// } | |
| 421 | +// else | |
| 422 | +// { | |
| 423 | +// failureNum++; | |
| 424 | +// failureMsg.append("<br/>" + failureNum + "、账号 " + user.getUserName() + " 已存在"); | |
| 425 | +// } | |
| 426 | +// } | |
| 427 | +// catch (Exception e) | |
| 428 | +// { | |
| 429 | +// failureNum++; | |
| 430 | +// String msg = "<br/>" + failureNum + "、账号 " + user.getUserName() + " 导入失败:"; | |
| 431 | +// failureMsg.append(msg + e.getMessage()); | |
| 432 | +// log.error(msg, e); | |
| 433 | +// } | |
| 434 | +// } | |
| 435 | +// if (failureNum > 0) | |
| 436 | +// { | |
| 437 | +// failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); | |
| 438 | +// throw new CustomException(failureMsg.toString()); | |
| 439 | +// } | |
| 440 | +// else | |
| 441 | +// { | |
| 442 | +// successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); | |
| 443 | +// } | |
| 444 | +// return successMsg.toString(); | |
| 445 | +// } | |
| 446 | + | |
| 447 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/util/date/DateUtils.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.util.date; | |
| 2 | + | |
| 3 | +import java.time.Duration; | |
| 4 | +import java.util.Date; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * 时间工具类 | |
| 8 | + */ | |
| 9 | +public class DateUtils { | |
| 10 | + | |
| 11 | + public static Date addTime(Duration duration) { | |
| 12 | + return new Date(System.currentTimeMillis() + duration.toMillis()); | |
| 13 | + } | |
| 14 | + | |
| 15 | + public static boolean isExpired(Date time) { | |
| 16 | + return System.currentTimeMillis() > time.getTime(); | |
| 17 | + } | |
| 18 | + | |
| 19 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/util/servlet/ServletUtils.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.util.servlet; | |
| 2 | + | |
| 3 | +import cn.hutool.extra.servlet.ServletUtil; | |
| 4 | +import com.alibaba.fastjson.JSON; | |
| 5 | +import org.springframework.http.MediaType; | |
| 6 | + | |
| 7 | + | |
| 8 | +import javax.servlet.http.HttpServletResponse; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * 客户端工具类 | |
| 12 | + * | |
| 13 | + * @author 芋道源码 | |
| 14 | + */ | |
| 15 | +public class ServletUtils { | |
| 16 | + | |
| 17 | + @SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码 | |
| 18 | + public static void writeJSON(HttpServletResponse response, Object object) { | |
| 19 | + String content = JSON.toJSONString(object); | |
| 20 | + ServletUtil.write(response, content, MediaType.APPLICATION_JSON_UTF8_VALUE); | |
| 21 | + } | |
| 22 | + | |
| 23 | +} | ... | ... |
src/main/resources/application.yaml