From 5304d865cb308deecdf6b58ac643f63907e588d3 Mon Sep 17 00:00:00 2001 From: Lierbao Date: Tue, 3 Jan 2023 10:25:54 +0800 Subject: [PATCH 1/6] init --- .../ningdatech/generator/config/GeneratorCodeConfig.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ningda-generator/src/main/java/com/ningdatech/generator/config/GeneratorCodeConfig.java b/ningda-generator/src/main/java/com/ningdatech/generator/config/GeneratorCodeConfig.java index 6a1f8f7..12e7db1 100644 --- a/ningda-generator/src/main/java/com/ningdatech/generator/config/GeneratorCodeConfig.java +++ b/ningda-generator/src/main/java/com/ningdatech/generator/config/GeneratorCodeConfig.java @@ -8,14 +8,14 @@ import java.util.Collections; /** * @description: 自动生成code代码 - * @author: liushuai - * @date: 2022/3/25 14:20 + * @author: liuxinxin + * @date: 2023/01/03 09:20 */ public class GeneratorCodeConfig { - private static final String PATH_LXX = "/Users/liuxinxin/IdeaProjects/car_rental/ningda-car-rental-api/src/main/java"; - private static final String PATH_YYD = "/Users/wendy/code project/java/car_rental/ningda-car-rental-api/src/main/java"; - private static final String PATH_LS = "/Users/qinxianyun/Documents/qin/ningdatech/car_rental/ningda-car-rental-api/src/main/java"; + private static final String PATH_LXX = ""; + private static final String PATH_YYD = ""; + private static final String PATH_LS = ""; private static final String PATH_ZPF = ""; private static final String PATH_CMM = ""; @@ -41,11 +41,11 @@ public class GeneratorCodeConfig { // 设置父包名 builder.parent("com.ningdatech") // 设置父包模块名 - .moduleName("rentalcar." + packageName) + .moduleName("pmapi." + packageName) // 设置mapperXml生成路径 .pathInfo(Collections.singletonMap(OutputFile.mapperXml, //设置自己的生成路径 - path + "/com/ningdatech/rentalcar/" + packageName + "/mapper")); + path + "/com/ningdatech/pmapi/" + packageName + "/mapper")); }) .strategyConfig(builder -> { builder.addTablePrefix("nd"); @@ -58,7 +58,7 @@ public class GeneratorCodeConfig { } public static void main(String[] args) { - generate("Liuxinxin", "order", PATH_YYD, "nd_order_status_change"); + generate("Liuxinxin", "null", PATH_LXX, "null"); } } From 5f38b02d351267a507e11dc4ffbba6807811d3f1 Mon Sep 17 00:00:00 2001 From: Lierbao Date: Tue, 3 Jan 2023 10:28:27 +0800 Subject: [PATCH 2/6] init --- .../common/handler/GlobalExceptionHandler.java | 62 ++++++++++++++++++++++ .../common/handler/GlobalResponseHandler.java | 56 +++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/common/handler/GlobalExceptionHandler.java create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/common/handler/GlobalResponseHandler.java diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/common/handler/GlobalExceptionHandler.java b/pmapi/src/main/java/com/ningdatech/pmapi/common/handler/GlobalExceptionHandler.java new file mode 100644 index 0000000..7cee5f1 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/common/handler/GlobalExceptionHandler.java @@ -0,0 +1,62 @@ +package com.ningdatech.pmapi.common.handler; + +import com.ningdatech.basic.enumeration.Status; +import com.ningdatech.basic.model.ApiResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.support.DefaultMessageSourceResolvable; +import org.springframework.http.HttpStatus; +import org.springframework.validation.BindException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.servlet.NoHandlerFoundException; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import java.util.stream.Collectors; + +/** + * @description: 统一错误处理 + * @author: liuxinxin + * @date: 2023/01/03 11:39 + */ +@Slf4j +@ControllerAdvice +@ResponseStatus(HttpStatus.BAD_REQUEST) +public class GlobalExceptionHandler { + + @ResponseBody + @ExceptionHandler(value = NoHandlerFoundException.class) + public ApiResponse noHandlerFoundException(NoHandlerFoundException e) { + log.error("【全局异常拦截】NoHandlerFoundException: 请求方法 {}, 请求路径 {}", e.getRequestURL(), e.getHttpMethod()); + return ApiResponse.ofStatus(Status.REQUEST_NOT_FOUND); + } + + @ResponseBody + @ExceptionHandler(value = {MethodArgumentNotValidException.class, BindException.class}) + public ApiResponse bindException(BindException e) { + String msg = e.getAllErrors().stream() + .map(DefaultMessageSourceResolvable::getDefaultMessage) + .collect(Collectors.joining(",")); + return ApiResponse.of(Status.BAD_REQUEST.getCode(), msg, null); + } + + @ResponseBody + @ExceptionHandler(value = ConstraintViolationException.class) + public ApiResponse constraintViolationException(ConstraintViolationException e) { + String msg = e.getConstraintViolations().stream() + .map(ConstraintViolation::getMessage) + .collect(Collectors.joining(",")); + return ApiResponse.of(Status.BAD_REQUEST.getCode(), msg, null); + } + + @ResponseBody + @ExceptionHandler(value = Exception.class) + public ApiResponse handlerException(Exception e) { + log.error("【全局异常拦截】: 异常信息 {}: {} ", e.getClass().getSimpleName(), e); + return ApiResponse.of(Status.BAD_REQUEST.getCode(), e.getMessage(), null); + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/common/handler/GlobalResponseHandler.java b/pmapi/src/main/java/com/ningdatech/pmapi/common/handler/GlobalResponseHandler.java new file mode 100644 index 0000000..d361210 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/common/handler/GlobalResponseHandler.java @@ -0,0 +1,56 @@ +package com.ningdatech.pmapi.common.handler; + +import cn.hutool.json.JSONUtil; +import com.ningdatech.basic.model.ApiResponse; +import org.springframework.core.MethodParameter; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; + +/** + * @Author liuxinxin + * @Date 2021/7/21 11:26 + * @Version 1.0 + **/ +@RestControllerAdvice(basePackages = {}) +public class GlobalResponseHandler implements ResponseBodyAdvice { + + private static final String SWAGGER_CLASS_PREFIX = "springfox.documentation"; + + @Override + public boolean supports(MethodParameter methodParameter, Class> aClass) { + return filter(methodParameter); + } + + @Override + public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, + Class> aClass, ServerHttpRequest serverHttpRequest, + ServerHttpResponse serverHttpResponse) { + ApiResponse apiResponse = ApiResponse.ofSuccess(o); + // 处理字符串时,遇到了类型转换的问题,debug一步一步跟踪,原来是对于字符串的ContentType是“text-plain”, + // ConverterType是StringHttpMessageConverter这个类型转换, + // 由于将结果封装成了自定义的ApiResponse类型,所以有ApiResponse转换成String报错 + // 所以需要对String类型的返回值单独进行处理 + if (o instanceof String) { + return JSONUtil.toJsonStr(apiResponse); + } + return ApiResponse.ofSuccess(o); + } + + private Boolean filter(MethodParameter methodParameter) { + Class declaringClass = methodParameter.getDeclaringClass(); + // swagger中的所有返回不进行统一封装 + if (declaringClass.getName().contains(SWAGGER_CLASS_PREFIX)) { + return false; + } + if (methodParameter.hasMethodAnnotation(ExceptionHandler.class)) { + return false; + } + // 如果本身就是使用ApiResponse返回,则不需要进行格式化 + return !methodParameter.getParameterType().equals(ApiResponse.class); + } +} From 447e8e3121c263f9ff401d325df5de6f7359c45b Mon Sep 17 00:00:00 2001 From: Lierbao Date: Tue, 3 Jan 2023 10:33:50 +0800 Subject: [PATCH 3/6] init --- .../security/auth/config/RedisSessionConfig.java | 49 ++++++++++++++++++++++ .../user/security/auth/constants/AuthTypeEnum.java | 47 +++++++++++++++++++++ .../auth/constants/SessionTimeConstant.java | 12 ++++++ 3 files changed, 108 insertions(+) create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/config/RedisSessionConfig.java create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/constants/AuthTypeEnum.java create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/constants/SessionTimeConstant.java diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/config/RedisSessionConfig.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/config/RedisSessionConfig.java new file mode 100644 index 0000000..af60fcd --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/config/RedisSessionConfig.java @@ -0,0 +1,49 @@ +//package com.ningdatech.pmapi.user.security.auth.config; +// +//import com.ningdatech.basic.util.StrPool; +//import org.springframework.beans.factory.annotation.Value; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +// +///** +// *

+// * 设置session的过期时间为一天 +// *

+// * +// * @Author LiuXinXin +// * @Date 2020/7/29 9:46 上午 +// * @Version 1.0 +// **/ +//@Configuration +//@EnableRedisHttpSession( +// maxInactiveIntervalInSeconds = RedisSessionConfig.SESSION_TIMEOUT, +// redisNamespace = RedisSessionConfig.REDIS_NAMESPACE +//) +//public class RedisSessionConfig { +// +// static final int SESSION_TIMEOUT = 24 * 60 * 60 * 10; +// +// static final String REDIS_NAMESPACE = "#{redisSessionConfig.getRedisNamespace()}"; +// +// @Value("${nd.cache.def.keyPrefix:}") +// private String keyPrefix; +// +// public String getRedisNamespace() { +// return (StrUtils.isBlank(keyPrefix) ? StrPool.EMPTY : keyPrefix + StrPool.COLON) + RedisIndexedSessionRepository.DEFAULT_NAMESPACE; +// } +// +// @Bean +// public CookieHttpSessionIdResolver sessionIdResolver() { +// // 创建 CookieHttpSessionIdResolver 对象 +// CookieHttpSessionIdResolver sessionIdResolver = new CookieHttpSessionIdResolver(); +// // 创建 DefaultCookieSerializer 对象 +// DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer(); +// +// // 设置到 sessionIdResolver 中 +// sessionIdResolver.setCookieSerializer(cookieSerializer); +// cookieSerializer.setCookieName(BizConst.COOKIE_KEY); +// cookieSerializer.setCookieMaxAge(SessionTimeConstant.SESSION_TIME_SECONDS); +// return sessionIdResolver; +// } +// +//} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/constants/AuthTypeEnum.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/constants/AuthTypeEnum.java new file mode 100644 index 0000000..927b5bd --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/constants/AuthTypeEnum.java @@ -0,0 +1,47 @@ +package com.ningdatech.pmapi.user.security.auth.constants; + +/** + * @author Liuxinxin + * @date 2021/7/30 下午2:10 + */ + +public enum AuthTypeEnum { + + /** + * 手机 + 密码的认证方式 + */ + PHONE_PASSWORD("phone_password"), + + /** + * 子账号 账号 + 密码的认证方式 + */ + ACCOUNT_PASSWORD("account_password"); + + private final String key; + + AuthTypeEnum(String key) { + this.key = key; + } + + public static boolean contains(String key) { + for (AuthTypeEnum value : AuthTypeEnum.values()) { + if (key.equals(value.getKey())) { + return true; + } + } + return false; + } + + public String getKey() { + return key; + } + + public static AuthTypeEnum of(String key) { + for (AuthTypeEnum value : AuthTypeEnum.values()) { + if (key.equals(value.getKey())) { + return value; + } + } + throw new RuntimeException(String.format("invalid AuthTypeEnum = %s", key)); + } +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/constants/SessionTimeConstant.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/constants/SessionTimeConstant.java new file mode 100644 index 0000000..a375263 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/constants/SessionTimeConstant.java @@ -0,0 +1,12 @@ +package com.ningdatech.pmapi.user.security.auth.constants; + +/** + * @Author LiuXinXin + * @Date 2022/2/17 12:59 上午 + * @Version 1.0 + **/ +public class SessionTimeConstant { + + public static final Integer SESSION_TIME_SECONDS = 24 * 60 * 60 * 10; + +} From 355064ee1d3d627afebe2128173d20932b9f452d Mon Sep 17 00:00:00 2001 From: Lierbao Date: Tue, 3 Jan 2023 10:46:41 +0800 Subject: [PATCH 4/6] security init --- .../pmapi/common/constants/BizConst.java | 98 ++++++++++++++++++++++ .../pmapi/common/errorcode/AppErrorCode.java | 17 ++++ .../user/security/auth/WebSecurityConfig.java | 97 +++++++++++++++++++++ .../user/security/auth/config/AuthProperties.java | 64 ++++++++++++++ .../auth/config/AuthenticationBeanConfig.java | 28 +++++++ .../security/auth/errorcode/AuthErrorCodeEnum.java | 34 ++++++++ .../handler/DefaultExpiredSessionStrategy.java | 41 +++++++++ .../auth/handler/DefaultLoginFailureHandler.java | 47 +++++++++++ .../auth/handler/DefaultLoginSuccessHandler.java | 35 ++++++++ .../auth/handler/DefaultLogoutSuccessHandler.java | 28 +++++++ .../user/security/auth/model/UserInfoDetails.java | 81 ++++++++++++++++++ .../password/PasswordLoginUserDetailService.java | 40 +++++++++ .../auth/password/UsernamePasswordAuthFilter.java | 74 ++++++++++++++++ .../password/UsernamePasswordAuthProvider.java | 64 ++++++++++++++ .../UsernamePasswordAuthSecurityConfig.java | 64 ++++++++++++++ .../auth/password/UsernamePasswordAuthToken.java | 76 +++++++++++++++++ 16 files changed, 888 insertions(+) create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/common/constants/BizConst.java create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/common/errorcode/AppErrorCode.java create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/WebSecurityConfig.java create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/config/AuthProperties.java create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/config/AuthenticationBeanConfig.java create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/errorcode/AuthErrorCodeEnum.java create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultExpiredSessionStrategy.java create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultLoginFailureHandler.java create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultLoginSuccessHandler.java create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultLogoutSuccessHandler.java create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/model/UserInfoDetails.java create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/PasswordLoginUserDetailService.java create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthFilter.java create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthProvider.java create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthSecurityConfig.java create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthToken.java diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/common/constants/BizConst.java b/pmapi/src/main/java/com/ningdatech/pmapi/common/constants/BizConst.java new file mode 100644 index 0000000..a4eb44c --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/common/constants/BizConst.java @@ -0,0 +1,98 @@ +package com.ningdatech.pmapi.common.constants; + +import com.ningdatech.basic.model.ApiResponse; + +import java.math.BigDecimal; + +/** + *

+ * 业务常量 + *

+ * + * @author WendyYang + * @since 13:42 2022/12/1 + */ +public interface BizConst { + + /** + * SQL查询一条 + */ + String LIMIT_1 = "limit 1"; + + String COOKIE_KEY = "ND_CAR_RENTAL_JSESSION"; + + /** + * 一小时秒数 + **/ + BigDecimal SECONDS_BY_HOUR = new BigDecimal(60 * 60); + + /** + * 十分钟的毫秒数 + */ + long MILLS_10_MIN = 1000L * 60 * 10; + + /** + * 中国行政区划编码 + */ + long ROOT_REGION_CODE = 100000L; + + /** + * 一级行政区划数量 + */ + int NUM_PROVINCE = 34; + + /** + * 默认的父id + */ + long PARENT_ID = 0L; + + /** + * 默认树层级 + */ + int TREE_GRADE = 0; + + /** + * 默认的排序 + */ + int SORT_VALUE = 0; + + /** + * 浙江省的region_id + */ + long ZJ_REGION_CODE = 330000L; + + /** + * 省/直辖市 level + */ + int GOV_L1 = 1; + + /** + * 市 level + */ + int GOV_L2 = 2; + + /** + * 区/县 level + */ + int GOV_L3 = 3; + + /** + * 密码正则:长度8-20位且至少包含大写字母、小写字母、数字或特殊符号中的任意三种 + */ + String REGEX_PASS = "^(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z\\W_]+$)(?![a-z0-9]+$)(?![a-z\\W_]+$)(?![0-9\\W_]+$)[a-zA-Z0-9\\W_]{8,20}$"; + + /** + * 租车费率 + */ + BigDecimal RATE_CAR_RENTAL = new BigDecimal("1.13"); + + /** + * 服务费率 + */ + BigDecimal RATE_SERVICE = new BigDecimal("0.0442"); + + ApiResponse UNAUTHENTICATED = ApiResponse.of(401, "用户未登录", null); + + int MAX_EXPORT_COUNT = 5000; + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/common/errorcode/AppErrorCode.java b/pmapi/src/main/java/com/ningdatech/pmapi/common/errorcode/AppErrorCode.java new file mode 100644 index 0000000..40a8f06 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/common/errorcode/AppErrorCode.java @@ -0,0 +1,17 @@ +package com.ningdatech.pmapi.common.errorcode; + +public enum AppErrorCode { + USER(100), + + AUTH(101); + + private final Integer code; + + AppErrorCode(Integer code) { + this.code = code; + } + + public Integer getCode() { + return code; + } +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/WebSecurityConfig.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/WebSecurityConfig.java new file mode 100644 index 0000000..58c2aa3 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/WebSecurityConfig.java @@ -0,0 +1,97 @@ +package com.ningdatech.pmapi.user.security.auth; + +import com.ningdatech.basic.util.NdJsonUtil; +import com.ningdatech.basic.util.StrPool; +import com.ningdatech.pmapi.common.constants.BizConst; +import com.ningdatech.pmapi.user.security.auth.config.AuthProperties; +import com.ningdatech.pmapi.user.security.auth.handler.DefaultExpiredSessionStrategy; +import com.ningdatech.pmapi.user.security.auth.password.UsernamePasswordAuthSecurityConfig; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import org.springframework.security.web.csrf.CookieCsrfTokenRepository; + +import java.io.PrintWriter; +import java.util.Map; +import java.util.Set; + +/** + * @Author LiuXinXin + * @Date 2020/7/28 4:14 下午 + * @Version 1.0 + */ +@Configuration +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + private final AuthProperties authProperties; + private final UsernamePasswordAuthSecurityConfig usernamePasswordAuthSecurityConfig; + private final LogoutSuccessHandler logoutSuccessHandler; + private final DefaultExpiredSessionStrategy defaultExpiredSessionStrategy; + + public WebSecurityConfig(AuthProperties authProperties, + UsernamePasswordAuthSecurityConfig usernamePasswordAuthSecurityConfig, + @Qualifier(value = "defaultLogoutSuccessHandler") LogoutSuccessHandler logoutSuccessHandler, + DefaultExpiredSessionStrategy defaultExpiredSessionStrategy) { + this.authProperties = authProperties; + this.usernamePasswordAuthSecurityConfig = usernamePasswordAuthSecurityConfig; + this.logoutSuccessHandler = logoutSuccessHandler; + this.defaultExpiredSessionStrategy = defaultExpiredSessionStrategy; + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + assemblerPreAuthUrls(http); + http.formLogin() + .and() + .exceptionHandling() + .authenticationEntryPoint(authenticationEntryPoint()) + .and().apply(usernamePasswordAuthSecurityConfig) + .and() + .authorizeRequests() + .antMatchers(authProperties.getIgnoreAuthUrlsArray()).permitAll() + .anyRequest() + .authenticated() + .and() + // 防止固定会话攻击,Spring security的默认配置就是如此: + // 登陆成功之后会创建一个新的会话,然后将旧的session信息复制到新的session中(客户端的sessionId变了) + .sessionManagement().invalidSessionUrl(authProperties.getInvalidSessionUrl()).sessionFixation() + .migrateSession() + // .invalidSessionStrategy(defaultInvalidSessionStrategy) + .maximumSessions(10).maxSessionsPreventsLogin(true).expiredSessionStrategy(defaultExpiredSessionStrategy) + .and().and().logout().logoutUrl(authProperties.getLogoutUrl()).logoutSuccessHandler(logoutSuccessHandler) + .deleteCookies(BizConst.COOKIE_KEY) + .and() + // 开启csrf验证,需要前端同步传入token + .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) + .ignoringAntMatchers(authProperties.getIgnoreCsrfUrlsArray()); + + + } + + private AuthenticationEntryPoint authenticationEntryPoint() { + return (request, response, authException) -> { + response.setContentType(StrPool.CONTENT_TYPE); + response.setStatus(HttpStatus.UNAUTHORIZED.value()); + PrintWriter writer = response.getWriter(); + writer.write(NdJsonUtil.getInstance().writeValueAsString(BizConst.UNAUTHENTICATED)); + writer.flush(); + writer.close(); + }; + } + + private void assemblerPreAuthUrls(HttpSecurity http) throws Exception { + Map roleArrayMap = authProperties.getRoleArrayMap(); + Set roleSet = roleArrayMap.keySet(); + for (String role : roleSet) { + String[] urlsArray = roleArrayMap.get(role); + if (urlsArray != null && urlsArray.length > 0) { + http.authorizeRequests().antMatchers(urlsArray).hasAuthority(role); + } + } + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/config/AuthProperties.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/config/AuthProperties.java new file mode 100644 index 0000000..8d0be9e --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/config/AuthProperties.java @@ -0,0 +1,64 @@ +package com.ningdatech.pmapi.user.security.auth.config; + +import cn.hutool.core.collection.CollectionUtil; +import com.ningdatech.basic.factory.PropertySourceFactory; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +import java.util.*; + +/** + * @Author LiuXinXin + * @Date 2020/8/1 6:06 下午 + * @Version 1.0 + **/ +@Configuration +@PropertySource(value = "classpath:security/auth-${spring.profiles.active}.yml" + , encoding = "utf-8", factory = PropertySourceFactory.class) +@ConfigurationProperties(prefix = "security.auth") +@Component +@Data +public class AuthProperties { + + private String authRequireUrl; + + private String invalidSessionUrl; + + private String passwordLoginUrl; + + private String logoutUrl; + + private List ignoreAuthUrls; + + private List ignoreCsrfUrls; + + private Map> roleMap = new HashMap<>(); + + public String[] getIgnoreAuthUrlsArray() { + String[] stringArray = new String[ignoreAuthUrls.size()]; + return ignoreAuthUrls.toArray(stringArray); + } + + public String[] getIgnoreCsrfUrlsArray() { + String[] stringArray = new String[ignoreCsrfUrls.size()]; + return ignoreCsrfUrls.toArray(stringArray); + } + + public Map getRoleArrayMap() { + Map roleArrayMap = new HashMap<>(); + if (Objects.nonNull(roleMap)) { + Set keySet = roleMap.keySet(); + for (String key : keySet) { + List urls = roleMap.get(key); + if (CollectionUtil.isNotEmpty(urls)) { + String[] stringArray = new String[urls.size()]; + roleArrayMap.put(key, urls.toArray(stringArray)); + } + } + } + return roleArrayMap; + } +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/config/AuthenticationBeanConfig.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/config/AuthenticationBeanConfig.java new file mode 100644 index 0000000..37d7da7 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/config/AuthenticationBeanConfig.java @@ -0,0 +1,28 @@ +package com.ningdatech.pmapi.user.security.auth.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.session.SessionRegistry; +import org.springframework.security.core.session.SessionRegistryImpl; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +/** + * @Author LiuXinXin + * @Date 2020/7/28 1:00 下午 + * @Version 1.0 + **/ +@Configuration +public class AuthenticationBeanConfig { + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public SessionRegistry sessionRegistry() { + return new SessionRegistryImpl(); + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/errorcode/AuthErrorCodeEnum.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/errorcode/AuthErrorCodeEnum.java new file mode 100644 index 0000000..5812ab1 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/errorcode/AuthErrorCodeEnum.java @@ -0,0 +1,34 @@ +package com.ningdatech.pmapi.user.security.auth.errorcode; + + +import com.ningdatech.pmapi.common.errorcode.AppErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author LiuXinXin + * @date 2021/7/30 上午10:59 + */ + +@AllArgsConstructor +@Getter +public enum AuthErrorCodeEnum { + + USERNAME_OR_PASSWORD_ERROR(AppErrorCode.AUTH.getCode() + "100000" + , "登陆时用户名或者密码错误"), + + ACCOUNT_ALREADY_EXIST_WHEN_REGISTER(AppErrorCode.AUTH.getCode() + "100001" + , "账号已经存在"), + + SESSION_EXPIRED(AppErrorCode.AUTH.getCode() + "100002" + , "用户session过期,登陆状态失效"); + + + private final String code; + + private final String msg; + + public Integer getCode() { + return Integer.valueOf(code); + } +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultExpiredSessionStrategy.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultExpiredSessionStrategy.java new file mode 100644 index 0000000..5024ae8 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultExpiredSessionStrategy.java @@ -0,0 +1,41 @@ +package com.ningdatech.pmapi.user.security.auth.handler; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ningdatech.basic.model.ApiResponse; +import com.ningdatech.pmapi.user.security.auth.errorcode.AuthErrorCodeEnum; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.web.session.SessionInformationExpiredEvent; +import org.springframework.security.web.session.SessionInformationExpiredStrategy; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @Author LiuXinXin + * @Date 2020/8/20 11:15 上午 + * @Version 1.0 + **/ +@Component +public class DefaultExpiredSessionStrategy implements SessionInformationExpiredStrategy { + + public static final Logger LOG = LoggerFactory.getLogger(DefaultExpiredSessionStrategy.class); + + @Autowired + private ObjectMapper objectMapper; + + @Override + public void onExpiredSessionDetected(SessionInformationExpiredEvent sessionInformationExpiredEvent) + throws IOException, ServletException { + if (LOG.isInfoEnabled()) { + LOG.info("session is expired"); + } + HttpServletResponse response = sessionInformationExpiredEvent.getResponse(); + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write(objectMapper.writeValueAsString( + ApiResponse.of(AuthErrorCodeEnum.SESSION_EXPIRED.getCode(), AuthErrorCodeEnum.SESSION_EXPIRED.getMsg(), null))); + } +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultLoginFailureHandler.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultLoginFailureHandler.java new file mode 100644 index 0000000..cf1ebc7 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultLoginFailureHandler.java @@ -0,0 +1,47 @@ +package com.ningdatech.pmapi.user.security.auth.handler; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ningdatech.basic.model.ApiResponse; +import com.ningdatech.pmapi.user.security.auth.errorcode.AuthErrorCodeEnum; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @Author LiuXinXin + * @Date 2020/8/3 8:32 下午 + * @Version 1.0 + **/ +@Component("defaultLoginFailureHandler") +public class DefaultLoginFailureHandler extends SimpleUrlAuthenticationFailureHandler { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, + AuthenticationException exception) throws IOException, ServletException { + response.setContentType("application/json;charset=UTF-8"); + int errorCode; + String errorMsg; + // 所有的认证异常都可以在这里添加,目前只支持用户名密码错误异常 + + if (exception instanceof BadCredentialsException || exception instanceof UsernameNotFoundException) { + errorCode = AuthErrorCodeEnum.USERNAME_OR_PASSWORD_ERROR.getCode(); + errorMsg = exception.getMessage(); + } else { + errorCode = ApiResponse.ERROR_CODE; + errorMsg = ApiResponse.ERROR_MSG; + } + response.setStatus(400); + response.getWriter() + .write(objectMapper.writeValueAsString(ApiResponse.of(errorCode, errorMsg, null))); + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultLoginSuccessHandler.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultLoginSuccessHandler.java new file mode 100644 index 0000000..d0c5ca9 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultLoginSuccessHandler.java @@ -0,0 +1,35 @@ +package com.ningdatech.pmapi.user.security.auth.handler; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ningdatech.basic.model.ApiResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Primary; +import org.springframework.http.MediaType; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @Author LiuXinXin + * @Date 2020/8/3 8:32 下午 + * @Version 1.0 + **/ +@Component("defaultLoginSuccessHandler") +@Primary +public class DefaultLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { + + @Autowired + private ObjectMapper objectMapper; + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) throws IOException { + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.getWriter().write(objectMapper.writeValueAsString(ApiResponse.ofSuccess())); + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultLogoutSuccessHandler.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultLogoutSuccessHandler.java new file mode 100644 index 0000000..b7e3a45 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultLogoutSuccessHandler.java @@ -0,0 +1,28 @@ +package com.ningdatech.pmapi.user.security.auth.handler; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ningdatech.basic.model.ApiResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * 默认的退出成功处理器 + */ +@Component("defaultLogoutSuccessHandler") +public class DefaultLogoutSuccessHandler implements LogoutSuccessHandler { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException { + // 退出成功后返回 和前端约定的Json + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().write(objectMapper.writeValueAsString(ApiResponse.ofSuccess())); + } +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/model/UserInfoDetails.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/model/UserInfoDetails.java new file mode 100644 index 0000000..b497f95 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/model/UserInfoDetails.java @@ -0,0 +1,81 @@ +package com.ningdatech.pmapi.user.security.auth.model; + +import cn.hutool.core.collection.CollectionUtil; +import com.ningdatech.basic.auth.AbstractLoginUser; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.List; + +/** + * @author LiuXinXin + * @date 2022/8/1 下午3:32 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class UserInfoDetails extends AbstractLoginUser implements UserDetails { + + private String realName; + + private String password; + + private String role; + + /** + * 区域code + */ + private Long regionCode; + + private Long companyId; + + /** + * 所负责公司id列表 + */ + private List responsibleCompanyIdList; + + /** + * 获取用户权限 + * + * @return + */ + @Override + public Collection getAuthorities() { + SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(this.role); + return CollectionUtil.toList(simpleGrantedAuthority); + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getUsername() { + return getIdentifier(); + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/PasswordLoginUserDetailService.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/PasswordLoginUserDetailService.java new file mode 100644 index 0000000..fdd0c52 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/PasswordLoginUserDetailService.java @@ -0,0 +1,40 @@ +package com.ningdatech.pmapi.user.security.auth.password; + + +import com.ningdatech.pmapi.user.security.auth.model.UserInfoDetails; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +/** + * @author LiuXinXin + * @date 2022/9/30 上午9:49 + */ + +@Service("passwordLoginUserDetailService") +@RequiredArgsConstructor +public class PasswordLoginUserDetailService implements UserDetailsService { + +// private final UserAuthLoginFacade userAuthLoginFacade; + + @Override + public UserInfoDetails loadUserByUsername(String username) throws UsernameNotFoundException { +// UserFullInfoDTO userFullInfoDTO = userAuthLoginFacade.queryUserInfoInPasswordAuth(username); +// if (Objects.isNull(userFullInfoDTO)) { +// throw new UsernameNotFoundException(String.format("%s user not exist", username)); +// } +// UserInfoDetails userInfoDetails = new UserInfoDetails(); +// userInfoDetails.setUserId(userFullInfoDTO.getUserId()); +// userInfoDetails.setUsername(userFullInfoDTO.getUsername()); +// userInfoDetails.setRealName(userFullInfoDTO.getRealName()); +// userInfoDetails.setRole(userFullInfoDTO.getRole()); +// userInfoDetails.setRegionCode(userFullInfoDTO.getRegionCode()); +// userInfoDetails.setCompanyId(userFullInfoDTO.getCompanyId()); +// userInfoDetails.setResponsibleCompanyIdList(userFullInfoDTO.getResponsibleCompanyIdList()); +// userInfoDetails.setIdentifier(userFullInfoDTO.getIdentifier()); +// userInfoDetails.setPassword(userFullInfoDTO.getCredential()); +// return userInfoDetails; + return null; + } +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthFilter.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthFilter.java new file mode 100644 index 0000000..dfddb8b --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthFilter.java @@ -0,0 +1,74 @@ +package com.ningdatech.pmapi.user.security.auth.password; + +import com.ningdatech.basic.exception.BizException; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.InternalAuthenticationServiceException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @Author LiuXinXin + * @Date 2020/8/3 8:46 下午 + * @Version 1.0 + **/ +public class UsernamePasswordAuthFilter extends AbstractAuthenticationProcessingFilter { + + private boolean postOnly = true; + + // ~ Constructors + // =================================================================================================== + + public UsernamePasswordAuthFilter(String processingUrl) { + super(new AntPathRequestMatcher(processingUrl, HttpMethod.POST.name())); + } + + // ~ Methods + // ======================================================================================================== + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) + throws AuthenticationException { + if (postOnly && !request.getMethod().equals(HttpMethod.POST.name())) { + throw new AuthenticationServiceException("请求方法错误"); + } + String usernameParameter = "username"; + String username = request.getParameter(usernameParameter); + String passwordParameter = "password"; + String password = request.getParameter(passwordParameter); + String loginPlatformParameter = "loginPlatform"; + String loginPlatform = request.getParameter(loginPlatformParameter); + if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { + throw new UsernameNotFoundException("用户名或密码不能为空"); + } + if (StringUtils.isBlank(loginPlatform)) { + throw new BizException("登录平台类型不能为空"); + } + username = username.trim(); + password = password.trim(); + try { + UsernamePasswordAuthToken authRequest = new UsernamePasswordAuthToken(username, password); + // Allow subclasses to set the "details" property + setDetails(request, authRequest); + return this.getAuthenticationManager().authenticate(authRequest); + } catch (AuthenticationException e) { + throw new BadCredentialsException("账号或密码错误"); + } catch (BizException e) { + throw new BadCredentialsException(e.getMessage()); + } catch (Exception e) { + throw new InternalAuthenticationServiceException("授权失败:", e); + } + } + + protected void setDetails(HttpServletRequest request, UsernamePasswordAuthToken authRequest) { + authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); + } +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthProvider.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthProvider.java new file mode 100644 index 0000000..8c7adae --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthProvider.java @@ -0,0 +1,64 @@ +package com.ningdatech.pmapi.user.security.auth.password; + +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.InternalAuthenticationServiceException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.password.PasswordEncoder; + +/** + * @Author LiuXinXin + * @Date 2020/8/3 8:55 下午 + * @Version 1.0 + **/ +public class UsernamePasswordAuthProvider implements AuthenticationProvider { + + private UserDetailsService userDetailsService; + + private PasswordEncoder passwordEncoder; + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + if (!(authentication instanceof UsernamePasswordAuthToken)) { + throw new RuntimeException("CustomAuthProvider 只支持 CustomAuthToken"); + } + UsernamePasswordAuthToken authenticationToken = (UsernamePasswordAuthToken) authentication; + + UserDetails user = userDetailsService.loadUserByUsername((String) authenticationToken.getPrincipal()); + if (user == null) { + throw new InternalAuthenticationServiceException("can not get user info!"); + } + additionalAuthenticationChecks(user, authenticationToken); + // 校验用户是否有当前端的登陆权限 + // 将用户定义的user放入token中,这样可以在session中查询到所有自定义的用户信息 + return new UsernamePasswordAuthToken(user, user.getPassword(), user.getAuthorities()); + } + + protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthToken authentication) + throws AuthenticationException { + if (authentication.getCredentials() == null) { + throw new BadCredentialsException("login fail! password is null"); + } + String presentedPassword = authentication.getCredentials().toString(); + if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { + throw new BadCredentialsException("login fail! password is error"); + } + } + + @Override + public boolean supports(Class authentication) { + return UsernamePasswordAuthToken.class.isAssignableFrom(authentication); + } + + public void setUserDetailsService(UserDetailsService userDetailsService) { + this.userDetailsService = userDetailsService; + } + + public void setPasswordEncoder(PasswordEncoder passwordEncoder) { + this.passwordEncoder = passwordEncoder; + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthSecurityConfig.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthSecurityConfig.java new file mode 100644 index 0000000..da48c8d --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthSecurityConfig.java @@ -0,0 +1,64 @@ +package com.ningdatech.pmapi.user.security.auth.password; + +import com.ningdatech.pmapi.user.security.auth.config.AuthProperties; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.SecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.DefaultSecurityFilterChain; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.stereotype.Component; + +/** + * 账号密码登陆的认证配置 + */ +@Component +public class UsernamePasswordAuthSecurityConfig + extends SecurityConfigurerAdapter { + + @Autowired + @Qualifier(value = "defaultLoginSuccessHandler") + protected AuthenticationSuccessHandler defaultLoginSuccessHandler; + + @Autowired + @Qualifier(value = "defaultLoginFailureHandler") + protected AuthenticationFailureHandler defaultLoginFailureHandler; + + @Autowired + @Qualifier(value = "passwordLoginUserDetailService") + private UserDetailsService passwordLoginUserDetailService; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Autowired + private AuthProperties authProperties; + + private AuthenticationManager authenticationManager; + + @Override + public void configure(HttpSecurity http) throws Exception { + UsernamePasswordAuthFilter usernamePasswordAuthFilter = + new UsernamePasswordAuthFilter(authProperties.getPasswordLoginUrl()); + authenticationManager = http.getSharedObject(AuthenticationManager.class); + usernamePasswordAuthFilter.setAuthenticationManager(authenticationManager); + usernamePasswordAuthFilter.setAuthenticationSuccessHandler(defaultLoginSuccessHandler); + usernamePasswordAuthFilter.setAuthenticationFailureHandler(defaultLoginFailureHandler); + + UsernamePasswordAuthProvider authenticationProvider = new UsernamePasswordAuthProvider(); + authenticationProvider.setUserDetailsService(passwordLoginUserDetailService); + // 确保对密码进行加密的encoder和解密的encoder相同 + authenticationProvider.setPasswordEncoder(passwordEncoder); + http.authenticationProvider(authenticationProvider).addFilterAfter(usernamePasswordAuthFilter, + UsernamePasswordAuthenticationFilter.class); + } + + public AuthenticationManager getAuthenticationManager() { + return authenticationManager; + } +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthToken.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthToken.java new file mode 100644 index 0000000..7be34c1 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthToken.java @@ -0,0 +1,76 @@ +package com.ningdatech.pmapi.user.security.auth.password; + +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.SpringSecurityCoreVersion; + +import java.util.Collection; + +/** + * @Author LiuXinXin + * @Date 2020/8/3 8:52 下午 + * @Version 1.0 + **/ +public class UsernamePasswordAuthToken extends AbstractAuthenticationToken { + + private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; + + private final Object principal; + + private final Object credentials; + + + /** + * This constructor can be safely used by any code that wishes to create a + * UsernamePasswordAuthenticationToken, as the {@link #isAuthenticated()} will return + * false. + */ + public UsernamePasswordAuthToken(String principal, String credentials) { + super(null); + this.principal = principal; + this.credentials = credentials; + setAuthenticated(false); + } + + /** + * This constructor should only be used by AuthenticationManager or AuthenticationProvider + * implementations that are satisfied with producing a trusted (i.e. {@link #isAuthenticated()} = true) + * authentication token. + * + * @param principal + * @param authorities + */ + public UsernamePasswordAuthToken(Object principal, Object credentials, + Collection authorities) { + super(authorities); + this.principal = principal; + this.credentials = credentials; + // must use super, as we override + super.setAuthenticated(true); + } + + @Override + public Object getCredentials() { + return this.credentials; + } + + @Override + public Object getPrincipal() { + return this.principal; + } + + @Override + public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { + if (isAuthenticated) { + throw new IllegalArgumentException( + "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead"); + } + super.setAuthenticated(false); + } + + @Override + public void eraseCredentials() { + super.eraseCredentials(); + } + +} From 2151807e18c27539281563027e6c4e00fc0babde Mon Sep 17 00:00:00 2001 From: Lierbao Date: Tue, 3 Jan 2023 10:58:59 +0800 Subject: [PATCH 5/6] init --- .../ningdatech/pmapi/user/manage/UserAuthLoginManage.java | 12 ++++++++++++ .../auth/password/PasswordLoginUserDetailService.java | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 pmapi/src/main/java/com/ningdatech/pmapi/user/manage/UserAuthLoginManage.java diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/manage/UserAuthLoginManage.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/manage/UserAuthLoginManage.java new file mode 100644 index 0000000..470c9c3 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/manage/UserAuthLoginManage.java @@ -0,0 +1,12 @@ +package com.ningdatech.pmapi.user.manage; + +import org.springframework.stereotype.Component; + +/** + * @author liuxinxin + * @date 2023/1/3 上午10:57 + */ + +@Component +public class UserAuthLoginManage { +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/PasswordLoginUserDetailService.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/PasswordLoginUserDetailService.java index fdd0c52..6dbe98e 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/PasswordLoginUserDetailService.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/PasswordLoginUserDetailService.java @@ -1,6 +1,7 @@ package com.ningdatech.pmapi.user.security.auth.password; +import com.ningdatech.pmapi.user.manage.UserAuthLoginManage; import com.ningdatech.pmapi.user.security.auth.model.UserInfoDetails; import lombok.RequiredArgsConstructor; import org.springframework.security.core.userdetails.UserDetailsService; @@ -16,7 +17,7 @@ import org.springframework.stereotype.Service; @RequiredArgsConstructor public class PasswordLoginUserDetailService implements UserDetailsService { -// private final UserAuthLoginFacade userAuthLoginFacade; + private final UserAuthLoginManage userAuthLoginManage; @Override public UserInfoDetails loadUserByUsername(String username) throws UsernameNotFoundException { From 21de4d76d041039977e74eb77fc5acbf8a2c39b4 Mon Sep 17 00:00:00 2001 From: Lierbao Date: Tue, 3 Jan 2023 11:04:45 +0800 Subject: [PATCH 6/6] init --- .../auth/password/UsernamePasswordAuthFilter.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthFilter.java b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthFilter.java index dfddb8b..d790723 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthFilter.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthFilter.java @@ -24,6 +24,10 @@ public class UsernamePasswordAuthFilter extends AbstractAuthenticationProcessing private boolean postOnly = true; + private static final String USERNAME_PARAMETER = "username"; + private static final String PASSWORD_PARAMETER = "password"; + + // ~ Constructors // =================================================================================================== @@ -40,18 +44,11 @@ public class UsernamePasswordAuthFilter extends AbstractAuthenticationProcessing if (postOnly && !request.getMethod().equals(HttpMethod.POST.name())) { throw new AuthenticationServiceException("请求方法错误"); } - String usernameParameter = "username"; - String username = request.getParameter(usernameParameter); - String passwordParameter = "password"; - String password = request.getParameter(passwordParameter); - String loginPlatformParameter = "loginPlatform"; - String loginPlatform = request.getParameter(loginPlatformParameter); + String username = request.getParameter(USERNAME_PARAMETER); + String password = request.getParameter(PASSWORD_PARAMETER); if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) { throw new UsernameNotFoundException("用户名或密码不能为空"); } - if (StringUtils.isBlank(loginPlatform)) { - throw new BizException("登录平台类型不能为空"); - } username = username.trim(); password = password.trim(); try {