Browse Source

security init

master
Lierbao 1 year ago
parent
commit
355064ee1d
16 changed files with 888 additions and 0 deletions
  1. +98
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/common/constants/BizConst.java
  2. +17
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/common/errorcode/AppErrorCode.java
  3. +97
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/WebSecurityConfig.java
  4. +64
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/config/AuthProperties.java
  5. +28
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/config/AuthenticationBeanConfig.java
  6. +34
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/errorcode/AuthErrorCodeEnum.java
  7. +41
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultExpiredSessionStrategy.java
  8. +47
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultLoginFailureHandler.java
  9. +35
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultLoginSuccessHandler.java
  10. +28
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultLogoutSuccessHandler.java
  11. +81
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/model/UserInfoDetails.java
  12. +40
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/PasswordLoginUserDetailService.java
  13. +74
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthFilter.java
  14. +64
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthProvider.java
  15. +64
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthSecurityConfig.java
  16. +76
    -0
      pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthToken.java

+ 98
- 0
pmapi/src/main/java/com/ningdatech/pmapi/common/constants/BizConst.java View File

@@ -0,0 +1,98 @@
package com.ningdatech.pmapi.common.constants;

import com.ningdatech.basic.model.ApiResponse;

import java.math.BigDecimal;

/**
* <p>
* 业务常量
* </p>
*
* @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<Void> UNAUTHENTICATED = ApiResponse.of(401, "用户未登录", null);

int MAX_EXPORT_COUNT = 5000;

}

+ 17
- 0
pmapi/src/main/java/com/ningdatech/pmapi/common/errorcode/AppErrorCode.java View File

@@ -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;
}
}

+ 97
- 0
pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/WebSecurityConfig.java View File

@@ -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<String, String[]> roleArrayMap = authProperties.getRoleArrayMap();
Set<String> roleSet = roleArrayMap.keySet();
for (String role : roleSet) {
String[] urlsArray = roleArrayMap.get(role);
if (urlsArray != null && urlsArray.length > 0) {
http.authorizeRequests().antMatchers(urlsArray).hasAuthority(role);
}
}
}

}

+ 64
- 0
pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/config/AuthProperties.java View File

@@ -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<String> ignoreAuthUrls;

private List<String> ignoreCsrfUrls;

private Map<String, List<String>> 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<String, String[]> getRoleArrayMap() {
Map<String, String[]> roleArrayMap = new HashMap<>();
if (Objects.nonNull(roleMap)) {
Set<String> keySet = roleMap.keySet();
for (String key : keySet) {
List<String> urls = roleMap.get(key);
if (CollectionUtil.isNotEmpty(urls)) {
String[] stringArray = new String[urls.size()];
roleArrayMap.put(key, urls.toArray(stringArray));
}
}
}
return roleArrayMap;
}
}

+ 28
- 0
pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/config/AuthenticationBeanConfig.java View File

@@ -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();
}

}

+ 34
- 0
pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/errorcode/AuthErrorCodeEnum.java View File

@@ -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);
}
}

+ 41
- 0
pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultExpiredSessionStrategy.java View File

@@ -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)));
}
}

+ 47
- 0
pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultLoginFailureHandler.java View File

@@ -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)));
}

}

+ 35
- 0
pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultLoginSuccessHandler.java View File

@@ -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()));
}

}

+ 28
- 0
pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/handler/DefaultLogoutSuccessHandler.java View File

@@ -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()));
}
}

+ 81
- 0
pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/model/UserInfoDetails.java View File

@@ -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<Long> 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;
}

}

+ 40
- 0
pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/PasswordLoginUserDetailService.java View File

@@ -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;
}
}

+ 74
- 0
pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthFilter.java View File

@@ -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));
}
}

+ 64
- 0
pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthProvider.java View File

@@ -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;
}

}

+ 64
- 0
pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthSecurityConfig.java View File

@@ -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<DefaultSecurityFilterChain, HttpSecurity> {

@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;
}
}

+ 76
- 0
pmapi/src/main/java/com/ningdatech/pmapi/user/security/auth/password/UsernamePasswordAuthToken.java View File

@@ -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
* <code>UsernamePasswordAuthenticationToken</code>, as the {@link #isAuthenticated()} will return
* <code>false</code>.
*/
public UsernamePasswordAuthToken(String principal, String credentials) {
super(null);
this.principal = principal;
this.credentials = credentials;
setAuthenticated(false);
}

/**
* This constructor should only be used by <code>AuthenticationManager</code> or <code>AuthenticationProvider</code>
* implementations that are satisfied with producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>)
* 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();
}

}

Loading…
Cancel
Save