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 extends GrantedAuthority> 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 extends GrantedAuthority> 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();
+ }
+
+}