mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-07-16 03:55:06 +08:00
去除 Spring Security 的 Member 的 loadUsername,使用自己定义的 login0 实现
This commit is contained in:
@ -1,13 +1,10 @@
|
||||
package cn.iocoder.yudao.framework.security.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.security.core.aop.PreAuthenticatedAspect;
|
||||
import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider;
|
||||
import cn.iocoder.yudao.framework.security.core.context.TransmittableThreadLocalSecurityContextHolderStrategy;
|
||||
import cn.iocoder.yudao.framework.security.core.filter.TokenAuthenticationFilter;
|
||||
import cn.iocoder.yudao.framework.security.core.handler.AccessDeniedHandlerImpl;
|
||||
import cn.iocoder.yudao.framework.security.core.handler.AuthenticationEntryPointImpl;
|
||||
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
|
||||
import cn.iocoder.yudao.framework.web.config.WebProperties;
|
||||
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
|
||||
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@ -20,7 +17,6 @@ import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Spring Security 自动配置类,主要用于相关组件的配置
|
||||
@ -76,19 +72,8 @@ public class YudaoSecurityAutoConfiguration {
|
||||
* Token 认证过滤器 Bean
|
||||
*/
|
||||
@Bean
|
||||
public TokenAuthenticationFilter authenticationTokenFilter(MultiUserDetailsAuthenticationProvider authenticationProvider,
|
||||
GlobalExceptionHandler globalExceptionHandler) {
|
||||
return new TokenAuthenticationFilter(securityProperties, authenticationProvider, globalExceptionHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 身份验证的 Provider Bean,通过它实现账号 + 密码的认证
|
||||
*/
|
||||
@Bean
|
||||
public MultiUserDetailsAuthenticationProvider authenticationProvider(
|
||||
List<SecurityAuthFrameworkService> securityFrameworkServices,
|
||||
WebProperties webProperties, PasswordEncoder passwordEncoder) {
|
||||
return new MultiUserDetailsAuthenticationProvider(securityFrameworkServices, webProperties, passwordEncoder);
|
||||
public TokenAuthenticationFilter authenticationTokenFilter(GlobalExceptionHandler globalExceptionHandler) {
|
||||
return new TokenAuthenticationFilter(securityProperties, globalExceptionHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,5 @@
|
||||
package cn.iocoder.yudao.framework.security.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider;
|
||||
import cn.iocoder.yudao.framework.security.core.filter.TokenAuthenticationFilter;
|
||||
import cn.iocoder.yudao.framework.web.config.WebProperties;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
@ -8,7 +7,6 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
@ -32,8 +30,6 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
|
||||
@Resource
|
||||
private WebProperties webProperties;
|
||||
|
||||
@Resource
|
||||
private MultiUserDetailsAuthenticationProvider authenticationProvider;
|
||||
/**
|
||||
* 认证失败处理类 Bean
|
||||
*/
|
||||
@ -69,14 +65,6 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
|
||||
return super.authenticationManagerBean();
|
||||
}
|
||||
|
||||
/**
|
||||
* 身份认证接口
|
||||
*/
|
||||
@Override
|
||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
auth.authenticationProvider(authenticationProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置 URL 的安全配置
|
||||
*
|
||||
@ -130,11 +118,7 @@ public class YudaoWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdap
|
||||
// 添加 JWT Filter
|
||||
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
}
|
||||
|
||||
private String buildAdminApi(String url) {
|
||||
return webProperties.getAdminApi().getPrefix() + url;
|
||||
}
|
||||
|
||||
|
||||
private String buildAppApi(String url) {
|
||||
return webProperties.getAppApi().getPrefix() + url;
|
||||
}
|
||||
|
@ -1,128 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.security.core.authentication;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
|
||||
import cn.iocoder.yudao.framework.web.config.WebProperties;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 支持多用户类型的 AuthenticationProvider 实现类
|
||||
*
|
||||
* 为什么不用 {@link org.springframework.security.authentication.ProviderManager} 呢?
|
||||
* 原因是,需要每个用户类型实现对应的 {@link AuthenticationProvider} + authentication,略显麻烦。实际,也是可以实现的。
|
||||
*
|
||||
* 另外,额外支持 verifyTokenAndRefresh 校验令牌、logout 登出、mockLogin 模拟登陆等操作。
|
||||
* 实际上,它就是 {@link SecurityAuthFrameworkService} 定义的三个接口。
|
||||
* 因为需要支持多种类型,所以需要根据请求的 URL,判断出对应的用户类型,从而使用对应的 SecurityAuthFrameworkService 是吸纳
|
||||
*
|
||||
* @see cn.iocoder.yudao.framework.common.enums.UserTypeEnum
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class MultiUserDetailsAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
|
||||
|
||||
private final Map<UserTypeEnum, SecurityAuthFrameworkService> services = new HashMap<>();
|
||||
|
||||
private final WebProperties properties;
|
||||
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
public MultiUserDetailsAuthenticationProvider(List<SecurityAuthFrameworkService> serviceList,
|
||||
WebProperties properties, PasswordEncoder passwordEncoder) {
|
||||
serviceList.forEach(service -> services.put(service.getUserType(), service));
|
||||
this.properties = properties;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
// ========== AuthenticationProvider 相关 ==========
|
||||
|
||||
@Override
|
||||
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
|
||||
throws AuthenticationException {
|
||||
// 执行用户的加载
|
||||
return selectService(authentication).loadUserByUsername(username);
|
||||
}
|
||||
|
||||
private SecurityAuthFrameworkService selectService(UsernamePasswordAuthenticationToken authentication) {
|
||||
// 第一步,获得用户类型
|
||||
UserTypeEnum userType = getUserType(authentication);
|
||||
// 第二步,获得 SecurityAuthFrameworkService
|
||||
SecurityAuthFrameworkService service = services.get(userType);
|
||||
Assert.notNull(service, "用户类型({}) 找不到 SecurityAuthFrameworkService 实现类", userType);
|
||||
return service;
|
||||
}
|
||||
|
||||
private UserTypeEnum getUserType(UsernamePasswordAuthenticationToken authentication) {
|
||||
Assert.isInstanceOf(MultiUsernamePasswordAuthenticationToken.class, authentication);
|
||||
MultiUsernamePasswordAuthenticationToken multiAuthentication = (MultiUsernamePasswordAuthenticationToken) authentication;
|
||||
UserTypeEnum userType = multiAuthentication.getUserType();
|
||||
Assert.notNull(userType, "用户类型不能为空");
|
||||
return userType;
|
||||
}
|
||||
|
||||
@Override // copy 自 DaoAuthenticationProvider 的 additionalAuthenticationChecks 方法
|
||||
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication)
|
||||
throws AuthenticationException {
|
||||
// 校验 credentials
|
||||
if (authentication.getCredentials() == null) {
|
||||
this.logger.debug("Failed to authenticate since no credentials provided");
|
||||
throw new BadCredentialsException(this.messages.getMessage(
|
||||
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
}
|
||||
// 校验 password
|
||||
String presentedPassword = authentication.getCredentials().toString();
|
||||
if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
|
||||
this.logger.debug("Failed to authenticate since password does not match stored value");
|
||||
throw new BadCredentialsException(this.messages.getMessage(
|
||||
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
}
|
||||
}
|
||||
|
||||
// ========== SecurityAuthFrameworkService 相关 ==========
|
||||
|
||||
/**
|
||||
* 校验 token 的有效性,并获取用户信息
|
||||
* 通过后,刷新 token 的过期时间
|
||||
*
|
||||
* @param request 请求
|
||||
* @param token token
|
||||
* @return 用户信息
|
||||
*/
|
||||
public LoginUser verifyTokenAndRefresh(HttpServletRequest request, String token) {
|
||||
return selectService(request).verifyTokenAndRefresh(token);
|
||||
}
|
||||
|
||||
private SecurityAuthFrameworkService selectService(HttpServletRequest request) {
|
||||
// 第一步,获得用户类型
|
||||
UserTypeEnum userType = getUserType(request);
|
||||
// 第二步,获得 SecurityAuthFrameworkService
|
||||
SecurityAuthFrameworkService service = services.get(userType);
|
||||
Assert.notNull(service, "URI({}) 用户类型({}) 找不到 SecurityAuthFrameworkService 实现类",
|
||||
request.getRequestURI(), userType);
|
||||
return service;
|
||||
}
|
||||
|
||||
private UserTypeEnum getUserType(HttpServletRequest request) {
|
||||
if (request.getRequestURI().startsWith(properties.getAdminApi().getPrefix())) {
|
||||
return UserTypeEnum.ADMIN;
|
||||
}
|
||||
if (request.getRequestURI().startsWith(properties.getAppApi().getPrefix())) {
|
||||
return UserTypeEnum.MEMBER;
|
||||
}
|
||||
throw new IllegalArgumentException(StrUtil.format("URI({}) 找不到匹配的用户类型", request.getRequestURI()));
|
||||
}
|
||||
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.security.core.authentication;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||
import lombok.Getter;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 支持多用户的 UsernamePasswordAuthenticationToken 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Getter
|
||||
public class MultiUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken {
|
||||
|
||||
/**
|
||||
* 用户类型
|
||||
*/
|
||||
private UserTypeEnum userType;
|
||||
|
||||
public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials) {
|
||||
super(principal, credentials);
|
||||
}
|
||||
|
||||
public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials,
|
||||
Collection<? extends GrantedAuthority> authorities) {
|
||||
super(principal, credentials, authorities);
|
||||
}
|
||||
|
||||
public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials, UserTypeEnum userType) {
|
||||
super(principal, credentials);
|
||||
this.userType = userType;
|
||||
}
|
||||
|
||||
public MultiUsernamePasswordAuthenticationToken(Object principal, Object credentials,
|
||||
Collection<? extends GrantedAuthority> authorities, UserTypeEnum userType) {
|
||||
super(principal, credentials, authorities);
|
||||
this.userType = userType;
|
||||
}
|
||||
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.security.core.authentication;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* 登录用户信息
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class SpringSecurityUser implements UserDetails {
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private Integer status;
|
||||
/**
|
||||
* 租户编号
|
||||
*/
|
||||
private Long tenantId;
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return CommonStatusEnum.ENABLE.getStatus().equals(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true; // 返回 true,不依赖 Spring Security 判断
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return true; // 返回 true,不依赖 Spring Security 判断
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true; // 返回 true,不依赖 Spring Security 判断
|
||||
}
|
||||
|
||||
}
|
@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||
import cn.iocoder.yudao.framework.security.config.SecurityProperties;
|
||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||
import cn.iocoder.yudao.framework.security.core.authentication.MultiUserDetailsAuthenticationProvider;
|
||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
|
||||
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
|
||||
@ -29,8 +28,6 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
private final SecurityProperties securityProperties;
|
||||
|
||||
private final MultiUserDetailsAuthenticationProvider authenticationProvider;
|
||||
|
||||
private final GlobalExceptionHandler globalExceptionHandler;
|
||||
|
||||
@Override
|
||||
@ -42,7 +39,7 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
||||
Integer userType = WebFrameworkUtils.getLoginUserType(request);
|
||||
try {
|
||||
// 验证 token 有效性
|
||||
LoginUser loginUser = authenticationProvider.verifyTokenAndRefresh(request, token);
|
||||
LoginUser loginUser = null; // TODO 芋艿:待实现
|
||||
// 模拟 Login 功能,方便日常开发调试
|
||||
if (loginUser == null) {
|
||||
loginUser = mockLoginUser(request, token, userType);
|
||||
|
Reference in New Issue
Block a user