Commit fc8eac548a698174554892f6bba8ca9758cf34f9
1 parent
ee9a358b
集成 Spring Security 组件,重构后,整体逻辑更加清晰
Showing
27 changed files
with
1821 additions
and
177 deletions
http-client.env.json
| 1 | { | 1 | { |
| 2 | "local": { | 2 | "local": { |
| 3 | "baseUrl": "http://127.0.0.1:8080/api", | 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
| @@ -27,6 +27,8 @@ yudao: | @@ -27,6 +27,8 @@ yudao: | ||
| 27 | token-secret: abcdefghijklmnopqrstuvwxyz | 27 | token-secret: abcdefghijklmnopqrstuvwxyz |
| 28 | token-timeout: 1d | 28 | token-timeout: 1d |
| 29 | session-timeout: 30m | 29 | session-timeout: 30m |
| 30 | + mock-enable: true | ||
| 31 | + mock-secret: yudaoyuanma | ||
| 30 | swagger: | 32 | swagger: |
| 31 | title: 管理后台 | 33 | title: 管理后台 |
| 32 | description: 提供管理员管理的所有功能 | 34 | description: 提供管理员管理的所有功能 |