diff --git a/eladmin-common/src/main/java/me/zhengjie/exception/handler/ApiError.java b/eladmin-common/src/main/java/me/zhengjie/exception/handler/ApiError.java index 977788c7..9568df8c 100644 --- a/eladmin-common/src/main/java/me/zhengjie/exception/handler/ApiError.java +++ b/eladmin-common/src/main/java/me/zhengjie/exception/handler/ApiError.java @@ -22,7 +22,7 @@ import lombok.Data; * @date 2018-11-23 */ @Data -class ApiError { +public class ApiError { private Integer status = 400; private Long timestamp; diff --git a/eladmin-common/src/main/java/me/zhengjie/utils/SecurityUtils.java b/eladmin-common/src/main/java/me/zhengjie/utils/SecurityUtils.java index aa4e3761..f44550c8 100644 --- a/eladmin-common/src/main/java/me/zhengjie/utils/SecurityUtils.java +++ b/eladmin-common/src/main/java/me/zhengjie/utils/SecurityUtils.java @@ -15,18 +15,24 @@ */ package me.zhengjie.utils; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.jwt.JWT; +import cn.hutool.jwt.JWTUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; -import me.zhengjie.exception.BadRequestException; import me.zhengjie.utils.enums.DataScopeEnum; -import org.springframework.http.HttpStatus; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; import java.util.List; +import java.util.Objects; /** * 获取当前登录的用户 @@ -34,8 +40,23 @@ import java.util.List; * @date 2019-01-17 */ @Slf4j +@Component public class SecurityUtils { + public static String header; + + public static String tokenStartWith; + + @Value("${jwt.header}") + public void setHeader(String header) { + SecurityUtils.header = header; + } + + @Value("${jwt.token-start-with}") + public void setTokenStartWith(String tokenStartWith) { + SecurityUtils.tokenStartWith = tokenStartWith; + } + /** * 获取当前登录的用户 * @return UserDetails @@ -45,34 +66,6 @@ public class SecurityUtils { return userDetailsService.loadUserByUsername(getCurrentUsername()); } - /** - * 获取系统用户名称 - * - * @return 系统用户名称 - */ - public static String getCurrentUsername() { - final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication == null) { - throw new BadRequestException(HttpStatus.UNAUTHORIZED, "当前登录状态过期"); - } - if (authentication.getPrincipal() instanceof UserDetails) { - UserDetails userDetails = (UserDetails) authentication.getPrincipal(); - return userDetails.getUsername(); - } - throw new BadRequestException(HttpStatus.UNAUTHORIZED, "找不到当前登录的信息"); - } - - /** - * 获取系统用户ID - * @return 系统用户ID - */ - public static Long getCurrentUserId() { - UserDetails userDetails = getCurrentUser(); - // 将 Java 对象转换为 JSONObject 对象 - JSONObject jsonObject = (JSONObject) JSON.toJSON(userDetails); - return jsonObject.getJSONObject("user").getLong("id"); - } - /** * 获取当前用户的数据权限 * @return / @@ -91,9 +84,62 @@ public class SecurityUtils { */ public static String getDataScopeType() { List dataScopes = getCurrentUserDataScope(); - if(dataScopes.size() != 0){ + if(CollUtil.isEmpty(dataScopes)){ return ""; } return DataScopeEnum.ALL.getValue(); } + + /** + * 获取用户ID + * @return 系统用户ID + */ + public static Long getCurrentUserId() { + return getCurrentUserId(getToken()); + } + + /** + * 获取用户ID + * @return 系统用户ID + */ + public static Long getCurrentUserId(String token) { + JWT jwt = JWTUtil.parseToken(token); + return Long.valueOf(jwt.getPayload("userId").toString()); + } + + /** + * 获取系统用户名称 + * + * @return 系统用户名称 + */ + public static String getCurrentUsername() { + return getCurrentUsername(getToken()); + } + + /** + * 获取系统用户名称 + * + * @return 系统用户名称 + */ + public static String getCurrentUsername(String token) { + JWT jwt = JWTUtil.parseToken(token); + return jwt.getPayload("sub").toString(); + } + + /** + * 获取Token + * @return / + */ + public static String getToken() { + HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder + .getRequestAttributes())).getRequest(); + String bearerToken = request.getHeader(header); + if (bearerToken != null && bearerToken.startsWith(tokenStartWith)) { + // 去掉令牌前缀 + return bearerToken.replace(tokenStartWith, ""); + } else { + log.debug("非法Token:{}", bearerToken); + } + return null; + } } diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/LoginProperties.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/CaptchaConfig.java similarity index 51% rename from eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/LoginProperties.java rename to eladmin-system/src/main/java/me/zhengjie/modules/security/config/CaptchaConfig.java index 0201a13d..94fdf2ce 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/LoginProperties.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/CaptchaConfig.java @@ -13,89 +13,100 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package me.zhengjie.modules.security.config.bean; +package me.zhengjie.modules.security.config; import com.wf.captcha.*; import com.wf.captcha.base.Captcha; import lombok.Data; -import me.zhengjie.exception.BadConfigurationException; +import lombok.Getter; +import me.zhengjie.exception.BadRequestException; +import me.zhengjie.modules.security.config.enums.LoginCodeEnum; import me.zhengjie.utils.StringUtils; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + import java.awt.*; -import java.util.Objects; /** - * 配置文件读取 - * + * 登录验证码配置信息 * @author liaojinlong - * @date loginCode.length0loginCode.length0/6/10 17:loginCode.length6 + * @date 2025-01-13 */ @Data -public class LoginProperties { +@Configuration +@ConfigurationProperties(prefix = "login.code") +public class CaptchaConfig { /** - * 账号单用户 登录 + * 验证码配置 */ - private boolean singleLogin = false; - - private LoginCode loginCode; - - public static final String cacheKey = "user-login-cache:"; - - public boolean isSingleLogin() { - return singleLogin; - } + @Getter + private LoginCodeEnum codeType; /** - * 获取验证码生产类 - * - * @return / + * 验证码有效期 分钟 */ - public Captcha getCaptcha() { - if (Objects.isNull(loginCode)) { - loginCode = new LoginCode(); - if (Objects.isNull(loginCode.getCodeType())) { - loginCode.setCodeType(LoginCodeEnum.ARITHMETIC); - } - } - return switchCaptcha(loginCode); - } + private Long expiration = 5L; + + /** + * 验证码内容长度 + */ + private int length = 4; + + /** + * 验证码宽度 + */ + private int width = 111; + + /** + * 验证码高度 + */ + private int height = 36; + + /** + * 验证码字体 + */ + private String fontName; + + /** + * 字体大小 + */ + private int fontSize = 25; /** * 依据配置信息生产验证码 - * - * @param loginCode 验证码配置信息 * @return / */ - private Captcha switchCaptcha(LoginCode loginCode) { + public Captcha getCaptcha() { Captcha captcha; - switch (loginCode.getCodeType()) { + switch (codeType) { case ARITHMETIC: // 算术类型 https://gitee.com/whvse/EasyCaptcha - captcha = new FixedArithmeticCaptcha(loginCode.getWidth(), loginCode.getHeight()); + captcha = new FixedArithmeticCaptcha(width, height); // 几位数运算,默认是两位 - captcha.setLen(loginCode.getLength()); + captcha.setLen(length); break; case CHINESE: - captcha = new ChineseCaptcha(loginCode.getWidth(), loginCode.getHeight()); - captcha.setLen(loginCode.getLength()); + captcha = new ChineseCaptcha(width, height); + captcha.setLen(length); break; case CHINESE_GIF: - captcha = new ChineseGifCaptcha(loginCode.getWidth(), loginCode.getHeight()); - captcha.setLen(loginCode.getLength()); + captcha = new ChineseGifCaptcha(width, height); + captcha.setLen(length); break; case GIF: - captcha = new GifCaptcha(loginCode.getWidth(), loginCode.getHeight()); - captcha.setLen(loginCode.getLength()); + captcha = new GifCaptcha(width, height); + captcha.setLen(length); break; case SPEC: - captcha = new SpecCaptcha(loginCode.getWidth(), loginCode.getHeight()); - captcha.setLen(loginCode.getLength()); + captcha = new SpecCaptcha(width, height); + captcha.setLen(length); break; default: - throw new BadConfigurationException("验证码配置信息错误!正确配置查看 LoginCodeEnum "); + throw new BadRequestException("验证码配置信息错误!正确配置查看 LoginCodeEnum "); } - if(StringUtils.isNotBlank(loginCode.getFontName())){ - captcha.setFont(new Font(loginCode.getFontName(), Font.PLAIN, loginCode.getFontSize())); + if(StringUtils.isNotBlank(fontName)){ + captcha.setFont(new Font(fontName, Font.PLAIN, fontSize)); } return captcha; } diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/ConfigBeanConfiguration.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/ConfigBeanConfiguration.java deleted file mode 100644 index 8cbc88dd..00000000 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/ConfigBeanConfiguration.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2019-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package me.zhengjie.modules.security.config; - -import me.zhengjie.modules.security.config.bean.LoginProperties; -import me.zhengjie.modules.security.config.bean.SecurityProperties; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * @apiNote 配置文件转换Pojo类的 统一配置 类 - * @author: liaojinlong - * @date: 2020/6/10 19:04 - */ -@Configuration -public class ConfigBeanConfiguration { - - @Bean - @ConfigurationProperties(prefix = "login") - public LoginProperties loginProperties() { - return new LoginProperties(); - } - - @Bean - @ConfigurationProperties(prefix = "jwt") - public SecurityProperties securityProperties() { - return new SecurityProperties(); - } -} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/LoginProperties.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/LoginProperties.java new file mode 100644 index 00000000..b38aabdf --- /dev/null +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/LoginProperties.java @@ -0,0 +1,39 @@ +/* + * Copyright 2019-2020 the original author or authors. + * + * Licensed under the Apache License, Version loginCode.length.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-loginCode.length.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package me.zhengjie.modules.security.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * 配置文件读取 + * + * @author liaojinlong + * @date loginCode.length0loginCode.length0/6/10 17:loginCode.length6 + */ +@Data +@Configuration +@ConfigurationProperties(prefix = "login") +public class LoginProperties { + + /** + * 账号单用户 登录 + */ + private boolean singleLogin = false; + + public static final String cacheKey = "user-login-cache:"; +} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/SecurityProperties.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityProperties.java similarity index 86% rename from eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/SecurityProperties.java rename to eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityProperties.java index 16ec3cf0..ac0dd501 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/SecurityProperties.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SecurityProperties.java @@ -13,9 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package me.zhengjie.modules.security.config.bean; +package me.zhengjie.modules.security.config; import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; /** * Jwt参数配置 @@ -24,6 +26,8 @@ import lombok.Data; * @date 2019年11月28日 */ @Data +@Configuration +@ConfigurationProperties(prefix = "jwt") public class SecurityProperties { /** diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SpringSecurityConfig.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SpringSecurityConfig.java index 0a776292..535b245a 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SpringSecurityConfig.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/SpringSecurityConfig.java @@ -16,7 +16,6 @@ package me.zhengjie.modules.security.config; import lombok.RequiredArgsConstructor; -import me.zhengjie.modules.security.config.bean.SecurityProperties; import me.zhengjie.modules.security.security.*; import me.zhengjie.modules.security.service.OnlineUserService; import me.zhengjie.modules.security.service.UserCacheManager; @@ -34,7 +33,6 @@ import org.springframework.security.config.core.GrantedAuthorityDefaults; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.filter.CorsFilter; import java.util.*; @@ -54,7 +52,6 @@ public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { private final ApplicationContext applicationContext; private final SecurityProperties properties; private final OnlineUserService onlineUserService; - private final UserCacheManager userCacheManager; @Bean GrantedAuthorityDefaults grantedAuthorityDefaults() { @@ -75,7 +72,7 @@ public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { httpSecurity // 禁用 CSRF .csrf().disable() - .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class) + .addFilter(corsFilter) // 授权异常 .exceptionHandling() .authenticationEntryPoint(authenticationErrorHandler) @@ -131,6 +128,6 @@ public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { } private TokenConfigurer securityConfigurerAdapter() { - return new TokenConfigurer(tokenProvider, properties, onlineUserService, userCacheManager); + return new TokenConfigurer(tokenProvider, properties, onlineUserService); } } diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/LoginCode.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/LoginCode.java deleted file mode 100644 index fefd252b..00000000 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/LoginCode.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2019-2020 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package me.zhengjie.modules.security.config.bean; - -import lombok.Data; - -/** - * 登录验证码配置信息 - * - * @author liaojinlong - * @date 2020/6/10 18:53 - */ -@Data -public class LoginCode { - - /** - * 验证码配置 - */ - private LoginCodeEnum codeType; - /** - * 验证码有效期 分钟 - */ - private Long expiration = 2L; - /** - * 验证码内容长度 - */ - private int length = 2; - /** - * 验证码宽度 - */ - private int width = 111; - /** - * 验证码高度 - */ - private int height = 36; - /** - * 验证码字体 - */ - private String fontName; - /** - * 字体大小 - */ - private int fontSize = 25; - - public LoginCodeEnum getCodeType() { - return codeType; - } -} diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/LoginCodeEnum.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/enums/LoginCodeEnum.java similarity index 94% rename from eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/LoginCodeEnum.java rename to eladmin-system/src/main/java/me/zhengjie/modules/security/config/enums/LoginCodeEnum.java index 685ccbbb..ce5632a5 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/config/bean/LoginCodeEnum.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/config/enums/LoginCodeEnum.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package me.zhengjie.modules.security.config.bean; +package me.zhengjie.modules.security.config.enums; /** * 验证码配置枚举 diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/rest/AuthorizationController.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/rest/AuthController.java similarity index 76% rename from eladmin-system/src/main/java/me/zhengjie/modules/security/rest/AuthorizationController.java rename to eladmin-system/src/main/java/me/zhengjie/modules/security/rest/AuthController.java index 4e03e577..de3cb9b6 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/rest/AuthorizationController.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/rest/AuthController.java @@ -27,10 +27,12 @@ import me.zhengjie.annotation.rest.AnonymousGetMapping; import me.zhengjie.annotation.rest.AnonymousPostMapping; import me.zhengjie.config.properties.RsaProperties; import me.zhengjie.exception.BadRequestException; -import me.zhengjie.modules.security.config.bean.LoginCodeEnum; -import me.zhengjie.modules.security.config.bean.LoginProperties; -import me.zhengjie.modules.security.config.bean.SecurityProperties; +import me.zhengjie.modules.security.config.CaptchaConfig; +import me.zhengjie.modules.security.config.enums.LoginCodeEnum; +import me.zhengjie.modules.security.config.LoginProperties; +import me.zhengjie.modules.security.config.SecurityProperties; import me.zhengjie.modules.security.security.TokenProvider; +import me.zhengjie.modules.security.service.UserDetailsServiceImpl; import me.zhengjie.modules.security.service.dto.AuthUserDto; import me.zhengjie.modules.security.service.dto.JwtUserDto; import me.zhengjie.modules.security.service.OnlineUserService; @@ -41,13 +43,12 @@ import me.zhengjie.utils.StringUtils; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; @@ -63,14 +64,15 @@ import java.util.concurrent.TimeUnit; @RequestMapping("/auth") @RequiredArgsConstructor @Api(tags = "系统:系统授权接口") -public class AuthorizationController { +public class AuthController { private final SecurityProperties properties; private final RedisUtils redisUtils; private final OnlineUserService onlineUserService; private final TokenProvider tokenProvider; - private final AuthenticationManagerBuilder authenticationManagerBuilder; - @Resource - private LoginProperties loginProperties; + private final LoginProperties loginProperties; + private final CaptchaConfig captchaConfig; + private final PasswordEncoder passwordEncoder; + private final UserDetailsServiceImpl userDetailsService; @Log("用户登录") @ApiOperation("登录授权") @@ -88,27 +90,29 @@ public class AuthorizationController { if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) { throw new BadRequestException("验证码错误"); } - UsernamePasswordAuthenticationToken authenticationToken = - new UsernamePasswordAuthenticationToken(authUser.getUsername(), password); - Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken); + // 获取用户信息 + JwtUserDto jwtUser = userDetailsService.loadUserByUsername(authUser.getUsername()); + // 验证用户密码 + if (!passwordEncoder.matches(password, jwtUser.getPassword())) { + throw new BadRequestException("登录密码错误"); + } + Authentication authentication = new UsernamePasswordAuthenticationToken(jwtUser, null, jwtUser.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authentication); - // 生成令牌与第三方系统获取令牌方式 - // UserDetails userDetails = userDetailsService.loadUserByUsername(userInfo.getUsername()); - // Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); - // SecurityContextHolder.getContext().setAuthentication(authentication); - String token = tokenProvider.createToken(authentication); - final JwtUserDto jwtUserDto = (JwtUserDto) authentication.getPrincipal(); + // 生成令牌 + String token = tokenProvider.createToken(jwtUser); + // 将密码设置为空 + jwtUser.setPassword(null); // 返回 token 与 用户信息 Map authInfo = new HashMap(2) {{ put("token", properties.getTokenStartWith() + token); - put("user", jwtUserDto); + put("user", jwtUser); }}; if (loginProperties.isSingleLogin()) { // 踢掉之前已经登录的token onlineUserService.kickOutForUsername(authUser.getUsername()); } // 保存在线信息 - onlineUserService.save(jwtUserDto, token, request); + onlineUserService.save(jwtUser, token, request); // 返回登录信息 return ResponseEntity.ok(authInfo); } @@ -116,14 +120,17 @@ public class AuthorizationController { @ApiOperation("获取用户信息") @GetMapping(value = "/info") public ResponseEntity getUserInfo() { - return ResponseEntity.ok(SecurityUtils.getCurrentUser()); + JwtUserDto jwtUser = (JwtUserDto) SecurityUtils.getCurrentUser(); + // 将密码设置为空 + jwtUser.setPassword(null); + return ResponseEntity.ok(jwtUser); } @ApiOperation("获取验证码") @AnonymousGetMapping(value = "/code") public ResponseEntity getCode() { // 获取运算的结果 - Captcha captcha = loginProperties.getCaptcha(); + Captcha captcha = captchaConfig.getCaptcha(); String uuid = properties.getCodeKey() + IdUtil.simpleUUID(); //当验证码类型为 arithmetic时且长度 >= 2 时,captcha.text()的结果有几率为浮点型 String captchaValue = captcha.text(); @@ -131,7 +138,7 @@ public class AuthorizationController { captchaValue = captchaValue.split("\\.")[0]; } // 保存 - redisUtils.set(uuid, captchaValue, loginProperties.getLoginCode().getExpiration(), TimeUnit.MINUTES); + redisUtils.set(uuid, captchaValue, captchaConfig.getExpiration(), TimeUnit.MINUTES); // 验证码信息 Map imgResult = new HashMap(2) {{ put("img", captcha.toBase64()); diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAccessDeniedHandler.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAccessDeniedHandler.java index 8b3f8c17..6d054733 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAccessDeniedHandler.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAccessDeniedHandler.java @@ -15,6 +15,9 @@ */ package me.zhengjie.modules.security.security; +import com.fasterxml.jackson.databind.ObjectMapper; +import me.zhengjie.exception.handler.ApiError; +import org.springframework.http.HttpStatus; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component; @@ -32,6 +35,10 @@ public class JwtAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException { //当用户在没有授权的情况下访问受保护的REST资源时,将调用此方法发送403 Forbidden响应 - response.sendError(HttpServletResponse.SC_FORBIDDEN, accessDeniedException.getMessage()); + response.setStatus(HttpStatus.FORBIDDEN.value()); + response.setContentType("application/json;charset=UTF-8"); + ObjectMapper objectMapper = new ObjectMapper(); + String jsonResponse = objectMapper.writeValueAsString(ApiError.error(HttpStatus.FORBIDDEN.value(), "禁止访问,您没有权限访问此资源")); + response.getWriter().write(jsonResponse); } } diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthenticationEntryPoint.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthenticationEntryPoint.java index f8815862..dba0963a 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthenticationEntryPoint.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/JwtAuthenticationEntryPoint.java @@ -15,10 +15,13 @@ */ package me.zhengjie.modules.security.security; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import me.zhengjie.exception.handler.ApiError; +import org.springframework.http.HttpStatus; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @@ -26,14 +29,18 @@ import java.io.IOException; /** * @author Zheng Jie */ +@Slf4j @Component public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override - public void commence(HttpServletRequest request, - HttpServletResponse response, - AuthenticationException authException) throws IOException { + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { // 当用户尝试访问安全的REST资源而不提供任何凭据时,将调用此方法发送401 响应 - response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException==null?"Unauthorized":authException.getMessage()); + int code = HttpStatus.UNAUTHORIZED.value(); + response.setStatus(code); + response.setContentType("application/json;charset=UTF-8"); + ObjectMapper objectMapper = new ObjectMapper(); + String jsonResponse = objectMapper.writeValueAsString(ApiError.error(HttpStatus.UNAUTHORIZED.value(), "登录状态已过期,请重新登录")); + response.getWriter().write(jsonResponse); } } diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenConfigurer.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenConfigurer.java index cff5e1ed..40f64cb6 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenConfigurer.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/security/TokenConfigurer.java @@ -16,7 +16,7 @@ package me.zhengjie.modules.security.security; import lombok.RequiredArgsConstructor; -import me.zhengjie.modules.security.config.bean.SecurityProperties; +import me.zhengjie.modules.security.config.SecurityProperties; import me.zhengjie.modules.security.service.OnlineUserService; import me.zhengjie.modules.security.service.UserCacheManager; import org.springframework.security.config.annotation.SecurityConfigurerAdapter; @@ -33,11 +33,10 @@ public class TokenConfigurer extends SecurityConfigurerAdapter claims = new HashMap<>(6); + // 设置用户ID + claims.put(AUTHORITIES_UID_KEY, user.getUser().getId()); + // 设置UUID,确保每次Token不一样 + claims.put(AUTHORITIES_UUID_KEY, IdUtil.simpleUUID()); return jwtBuilder - // 加入ID确保生成的 Token 都不一致 - .setId(IdUtil.simpleUUID()) - .claim(AUTHORITIES_KEY, authentication.getName()) - .setSubject(authentication.getName()) + .setClaims(claims) + .setSubject(user.getUsername()) .compact(); } diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/OnlineUserService.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/OnlineUserService.java index c496d8cd..18096fb4 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/OnlineUserService.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/OnlineUserService.java @@ -19,12 +19,11 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import me.zhengjie.modules.security.security.TokenProvider; import me.zhengjie.utils.PageResult; -import me.zhengjie.modules.security.config.bean.SecurityProperties; +import me.zhengjie.modules.security.config.SecurityProperties; import me.zhengjie.modules.security.service.dto.JwtUserDto; import me.zhengjie.modules.security.service.dto.OnlineUserDto; import me.zhengjie.utils.*; import org.springframework.data.domain.Pageable; -import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserCacheManager.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserCacheManager.java index 03870dc0..ef0e8ec2 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserCacheManager.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserCacheManager.java @@ -16,7 +16,7 @@ package me.zhengjie.modules.security.service; import cn.hutool.core.util.RandomUtil; -import me.zhengjie.modules.security.config.bean.LoginProperties; +import me.zhengjie.modules.security.config.LoginProperties; import me.zhengjie.modules.security.service.dto.JwtUserDto; import me.zhengjie.utils.RedisUtils; import me.zhengjie.utils.StringUtils; diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserDetailsServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserDetailsServiceImpl.java index 70db5df0..7aa0ba2b 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserDetailsServiceImpl.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/UserDetailsServiceImpl.java @@ -18,14 +18,12 @@ package me.zhengjie.modules.security.service; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import me.zhengjie.exception.BadRequestException; -import me.zhengjie.exception.EntityNotFoundException; import me.zhengjie.modules.security.service.dto.JwtUserDto; import me.zhengjie.modules.system.service.DataService; import me.zhengjie.modules.system.service.RoleService; import me.zhengjie.modules.system.service.UserService; import me.zhengjie.modules.system.service.dto.UserLoginDto; import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; /** @@ -45,24 +43,14 @@ public class UserDetailsServiceImpl implements UserDetailsService { public JwtUserDto loadUserByUsername(String username) { JwtUserDto jwtUserDto = userCacheManager.getUserCache(username); if(jwtUserDto == null){ - UserLoginDto user; - try { - user = userService.getLoginData(username); - } catch (EntityNotFoundException e) { - // SpringSecurity会自动转换UsernameNotFoundException为BadCredentialsException - throw new UsernameNotFoundException(username, e); - } + UserLoginDto user = userService.getLoginData(username); if (user == null) { - throw new UsernameNotFoundException(""); + throw new BadRequestException("用户不存在"); } else { if (!user.getEnabled()) { throw new BadRequestException("账号未激活!"); } - jwtUserDto = new JwtUserDto( - user, - dataService.getDeptIds(user), - roleService.mapToGrantedAuthorities(user) - ); + jwtUserDto = new JwtUserDto(user, dataService.getDeptIds(user), roleService.buildAuthorities(user), user.getPassword()); // 添加缓存数据 userCacheManager.addUserCache(username, jwtUserDto); } diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/JwtUserDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/JwtUserDto.java index 5aa0c45f..cf25c8aa 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/JwtUserDto.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/security/service/dto/JwtUserDto.java @@ -16,8 +16,10 @@ package me.zhengjie.modules.security.service.dto; import com.alibaba.fastjson.annotation.JSONField; +import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.Setter; import me.zhengjie.modules.system.service.dto.UserLoginDto; import org.springframework.security.core.userdetails.UserDetails; import java.util.List; @@ -38,6 +40,10 @@ public class JwtUserDto implements UserDetails { private final List authorities; + @Setter + @ApiModelProperty(value = "密码") + private String password; + public Set getRoles() { return authorities.stream().map(AuthorityDto::getAuthority).collect(Collectors.toSet()); } diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/RoleService.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/RoleService.java index 6bc8cce7..9901cd75 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/RoleService.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/RoleService.java @@ -120,7 +120,7 @@ public interface RoleService { * @param user 用户信息 * @return 权限信息 */ - List mapToGrantedAuthorities(UserDto user); + List buildAuthorities(UserDto user); /** * 验证是否被用户关联 diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserLoginDto.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserLoginDto.java index 13a51f77..e5b101f0 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserLoginDto.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/dto/UserLoginDto.java @@ -15,6 +15,8 @@ */ package me.zhengjie.modules.system.service.dto; +import com.alibaba.fastjson.annotation.JSONField; + /** * @author Zheng Jie * @description 用户缓存时使用 @@ -22,6 +24,7 @@ package me.zhengjie.modules.system.service.dto; **/ public class UserLoginDto extends UserDto { + @JSONField(serialize = false) private String password; private Boolean isAdmin; diff --git a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/RoleServiceImpl.java b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/RoleServiceImpl.java index e416aa89..9a12c520 100644 --- a/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/RoleServiceImpl.java +++ b/eladmin-system/src/main/java/me/zhengjie/modules/system/service/impl/RoleServiceImpl.java @@ -164,7 +164,7 @@ public class RoleServiceImpl implements RoleService { @Override @Cacheable(key = "'auth:' + #p0.id") - public List mapToGrantedAuthorities(UserDto user) { + public List buildAuthorities(UserDto user) { Set permissions = new HashSet<>(); // 如果是管理员直接返回 if (user.getIsAdmin()) { diff --git a/eladmin-system/src/main/resources/config/application-dev.yml b/eladmin-system/src/main/resources/config/application-dev.yml index fa8b7ede..eee8ddaf 100644 --- a/eladmin-system/src/main/resources/config/application-dev.yml +++ b/eladmin-system/src/main/resources/config/application-dev.yml @@ -60,7 +60,7 @@ login: # 存活时间/秒 idle-time: 21600 # 验证码 - login-code: + code: # 验证码类型配置 查看 LoginProperties 类 code-type: arithmetic # 登录图形验证码有效时间/分钟 diff --git a/eladmin-system/src/main/resources/config/application-prod.yml b/eladmin-system/src/main/resources/config/application-prod.yml index 4d9d2aab..748c0101 100644 --- a/eladmin-system/src/main/resources/config/application-prod.yml +++ b/eladmin-system/src/main/resources/config/application-prod.yml @@ -64,7 +64,7 @@ login: # 存活时间/秒 idle-time: 21600 # 验证码 - login-code: + code: # 验证码类型配置 查看 LoginProperties 类 code-type: arithmetic # 登录图形验证码有效时间/分钟