ucard login

This commit is contained in:
ran.shang
2025-08-12 10:41:06 +08:00
parent 4526fc1cf8
commit bf59ce0a10
11 changed files with 290 additions and 35 deletions

View File

@@ -1,5 +1,6 @@
package com.newyetai.security.config;
import com.newyetai.security.ucard.UcardAuthenticationProvider;
import org.springframework.boot.autoconfigure.AutoConfigurationPackage;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -84,5 +85,11 @@ public class SecurityConfiguration {
SsbAuthenticationProvider ssbAuthenticationProvider(UserLoginService userLoginService) {
return new SsbAuthenticationProvider(userLoginService);
}
@Bean
@ConditionalOnBean(UserLoginService.class)
UcardAuthenticationProvider ucardAuthenticationProvider(UserLoginService userLoginService) {
return new UcardAuthenticationProvider(userLoginService);
}
}

View File

@@ -1,5 +1,7 @@
package com.newyetai.security.config;
import com.newyetai.security.ucard.UcardAuthenticationFilter;
import com.newyetai.security.ucard.UcardAuthenticationProvider;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
@@ -43,7 +45,7 @@ import lombok.extern.slf4j.Slf4j;
/**
* security配置
*
*
* @author on 2019-11-05
*/
@Slf4j
@@ -79,6 +81,9 @@ public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Autowired
private SsbAuthenticationProvider ssbAuthenticationProvider;
@Autowired(required = false)
private UcardAuthenticationProvider ucardAuthenticationProvider;
@Autowired
private SwaggerConfig swaggerConfig;
@@ -92,9 +97,10 @@ public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 添加cas认证提供者
auth.authenticationProvider(upasswordAuthenticationProvider)
.authenticationProvider(mobileAuthenticationProvider)
.authenticationProvider(tokenAuthenticationProvider)
.authenticationProvider(ssbAuthenticationProvider)
.authenticationProvider(mobileAuthenticationProvider)
.authenticationProvider(tokenAuthenticationProvider)
.authenticationProvider(ssbAuthenticationProvider)
.authenticationProvider(ucardAuthenticationProvider);
;
}
@@ -146,6 +152,8 @@ public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
http.addFilterAfter(mobileAuthenticationFilter(), UpasswordAuthenticationFilter.class);
http.addFilterAfter(tokenAuthenticationFilter(), UpasswordAuthenticationFilter.class);
http.addFilterAfter(ssbAuthenticationFilter(), UpasswordAuthenticationFilter.class);
http.addFilterAfter(ucardAuthenticationFilter(), UpasswordAuthenticationFilter.class);
// 禁用CSRF
http.csrf().disable();
@@ -171,14 +179,14 @@ public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
}
}
private JwtAuthenticationTokenFilter createJwtAuthenticationTokenFilter() {
return new JwtAuthenticationTokenFilter(securityProperties);
}
/**
* 拦截器
*
*
* @return
* @throws Exception
* @author sr on 2019-11-28
@@ -196,11 +204,11 @@ public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
/**
* 手机号登录请求过滤器
*
*
* @param authenticationManager
* @return
* @author sr on 2020-06-04
* @throws Exception
* @author sr on 2020-06-04
*/
private MobileAuthenticationFilter mobileAuthenticationFilter() throws Exception {
MobileAuthenticationFilter filter = new MobileAuthenticationFilter();
@@ -216,9 +224,10 @@ public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
/**
* 账号密码
*
* @return
* @throws Exception
* @author sr on 2021-06-22
* @author sr on 2021-06-22
*/
private UpasswordAuthenticationFilter upasswordAuthenticationFilter() throws Exception {
UpasswordAuthenticationFilter filter = new UpasswordAuthenticationFilter();
@@ -228,9 +237,10 @@ public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
/**
* 临时token
*
* @return
* @throws Exception
* @author sr on 2021-06-22
* @author sr on 2021-06-22
*/
private TokenAuthenticationFilter tokenAuthenticationFilter() throws Exception {
TokenAuthenticationFilter filter = new TokenAuthenticationFilter();
@@ -244,4 +254,17 @@ public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
return filter;
}
/**
* 身份证登录
* @return
* @throws Exception
* @author sr on 2024-09-23
*/
private UcardAuthenticationFilter ucardAuthenticationFilter() throws Exception {
UcardAuthenticationFilter filter = new UcardAuthenticationFilter();
addHandler(filter);
return filter;
}
}

View File

@@ -0,0 +1,60 @@
package com.newyetai.security.ucard;
import com.newyetai.usercenter.user.login.dto.LoginUCardDTO;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
/**
* 账号密码认证过滤器,指定请求组装token,调起认证
*
* @author sr on 2021-06-21
*/
public class UcardAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private boolean postOnly = true;
public static final String REQUEST_METHOD = "POST";
public static final AntPathRequestMatcher requiresAuthenticationRequestMatcher =
new AntPathRequestMatcher("/login_ucard", REQUEST_METHOD);
public static final String LOGIN_REAL_NAME_KEY = "realName";
public static final String LOGIN_CARD_NO_KEY = "cardNo";
public UcardAuthenticationFilter() {
super(requiresAuthenticationRequestMatcher);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
if (postOnly && !REQUEST_METHOD.equals(request.getMethod())) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String realName = request.getParameter(LOGIN_REAL_NAME_KEY);
String cardNo = request.getParameter(LOGIN_CARD_NO_KEY);
realName = realName == null ? "" : realName.trim();
cardNo = cardNo == null ? "" : cardNo.trim();
AbstractAuthenticationToken authRequest = new UcardAuthenticationToken(
LoginUCardDTO.builder().realName(realName).cardNo(cardNo).build(), cardNo);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
protected void setDetails(HttpServletRequest request, AbstractAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
}

View File

@@ -0,0 +1,64 @@
package com.newyetai.security.ucard;
import com.newyetai.common.exception.util.Assert;
import com.newyetai.common.validation.util.ValidateUtils;
import com.newyetai.security.dto.SecurityUserDTO;
import com.newyetai.security.expection.BadLoginException;
import com.newyetai.security.mapstruct.UserMapMapper;
import com.newyetai.usercenter.user.login.dto.LoginUCardDTO;
import com.newyetai.usercenter.user.login.dto.UserDetailDTO;
import com.newyetai.usercenter.user.login.service.UserLoginService;
import java.util.HashSet;
import java.util.Set;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
/**
* 账号密码认证provider,比对信息是否匹配
*
* @author sr on 2021-06-21
*/
public class UcardAuthenticationProvider implements AuthenticationProvider {
private UserLoginService userLoginService;
public UcardAuthenticationProvider(UserLoginService userLoginService) {
this.userLoginService = userLoginService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
LoginUCardDTO loginUCardDTO =
(LoginUCardDTO) authentication.getPrincipal();
String errMsg = ValidateUtils.validate(LoginUCardDTO.class);
Assert.isTrue(errMsg == null, new BadLoginException(errMsg));
SecurityUserDTO loadedUser = null;
try {
UserDetailDTO userDetailDTO = this.userLoginService.loadUser(loginUCardDTO);
loadedUser = UserMapMapper.INSTANCE.dtoToDto(userDetailDTO);
} catch (BadLoginException be) {
throw be;
} catch (Exception e) {
throw new BadLoginException(e.getMessage(), e);
}
UcardAuthenticationToken result =
new UcardAuthenticationToken(loadedUser, listUserGrantedAuthorities(loadedUser));
result.setDetails(authentication.getDetails());
return result;
}
@Override
public boolean supports(Class<?> authentication) {
return (UcardAuthenticationToken.class.isAssignableFrom(authentication));
}
private Set<GrantedAuthority> listUserGrantedAuthorities(UserDetails userDetails) {
// 什么都不用做,访问资源鉴权的时会拿userDetail去缓存获取对应权限
return new HashSet<GrantedAuthority>();
}
}

View File

@@ -0,0 +1,23 @@
package com.newyetai.security.ucard;
import java.util.Collection;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
/**
* 账号密码认证信息
* @author sr on 2021-06-21
*/
public class UcardAuthenticationToken extends UsernamePasswordAuthenticationToken {
public UcardAuthenticationToken(Object principal, Object credentials) {
super(principal, credentials);
}
public UcardAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(principal, credentials, authorities);
}
}

View File

@@ -35,5 +35,8 @@ public class UserDetailQueryDTO implements Serializable {
private String phone;
private Long orgId;
/** 身份证号 **/
private String cardNo;
}

View File

@@ -0,0 +1,29 @@
package com.newyetai.usercenter.user.login.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import javax.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
@ApiModel(description = "身份证登录")
public class LoginUCardDTO implements Serializable{
/** **/
private static final long serialVersionUID = -5406549736684905311L;
@ApiModelProperty("姓名")
@NotBlank(message = "姓名不能为空")
private String realName;
@ApiModelProperty("身份证号")
@NotBlank(message = "身份证号不能为空")
private String cardNo;
}

View File

@@ -9,7 +9,7 @@ import java.util.List;
/**
* 用户
*
*
* @author sr on 2019-11-05
*/
@Builder
@@ -24,13 +24,13 @@ public class UserDetailDTO implements Serializable{
@ApiModelProperty("用户id")
private Long id;
@ApiModelProperty("是否超管")
private Boolean supeiror ;
@ApiModelProperty("类型")
private Integer type;
/** ----------------仅限机构用户--------------- **/
@ApiModelProperty("当前操作机构")
private Long orgId;
@@ -40,7 +40,7 @@ public class UserDetailDTO implements Serializable{
@ApiModelProperty("当前操作机构类型")
private String orgType;
@ApiModelProperty("是否机构管理员")
private Boolean administrator;
/** ----------------仅限机构用户--------------- **/
@@ -55,7 +55,7 @@ public class UserDetailDTO implements Serializable{
@ApiModelProperty("是否启用")
private Boolean enabled;
@ApiModelProperty("是否新注册")
private Boolean register;
@@ -70,7 +70,7 @@ public class UserDetailDTO implements Serializable{
@ApiModelProperty("凭据是否未过期")
private Boolean credentialsNonExpired;
@JsonIgnore
@ApiModelProperty("真实姓名")
private String realName;
@@ -78,10 +78,13 @@ public class UserDetailDTO implements Serializable{
@JsonIgnore
@ApiModelProperty("身份证号")
private String cardNo;
@ApiModelProperty("手机号绑定状态")
private Boolean phoneBinding;
@ApiModelProperty("是否实名")
private Boolean authenticated ;
@ApiModelProperty("机构列表")
@JsonIgnore
private List<OrgDetailDTO> orgs;

View File

@@ -1,5 +1,6 @@
package com.newyetai.usercenter.user.login.service;
import com.newyetai.usercenter.user.login.dto.LoginUCardDTO;
import javax.servlet.http.HttpServletRequest;
import com.newyetai.security.service.IUserSecurityService;
@@ -16,28 +17,30 @@ import com.newyetai.usercenter.user.login.dto.UserDetailDTO;
/**
* 用户登录
* @author sr on 2021-06-17
* @author sr on 2021-06-17
*/
public interface UserLoginService extends IUserSecurityService {
UserDetailDTO loadUser(LoginUsernamePasswordDTO loginUsernamePasswordDTO);
UserDetailDTO loadUser(LoginUCardDTO loginUCardDTO);
UserDetailDTO loadUser(LoginPhoneDTO loginPhoneDTO);
UserDetailDTO loadUser(LoginPhoneToken loginPhoneToken);
UserDetailDTO loadUser(LoginUserToken loginUserToken);
UserDetailDTO findUserDetailDTO(UserDetailQueryDTO userDetailQueryDto);
AuthClientDTO findAuthClientDTO(AuthClientQueryDTO authClientQueryDTO);
UserDetailDTO findUserDetailDTO(AuthDetailLoginDTO authDetailDTO);
/**
* 如果需激活请求,判断上下文用户激活状态,如果待激活,查询数据库用户状态,直接拦截
* @return
* @author sr on 2021-06-30
* @author sr on 2021-06-30
*/
void checkActive(HttpServletRequest request);

View File

@@ -1,5 +1,8 @@
package com.newyetai.usercenter.user.login.service.impl;
import com.newyetai.usercenter.user.dto.SuperRealNameTwoAuthDTO;
import com.newyetai.usercenter.user.login.dto.LoginUCardDTO;
import com.newyetai.usercenter.user.service.UserRealNameTwoVerificationService;
import java.util.List;
import java.util.stream.Collectors;
@@ -97,6 +100,9 @@ public class UserLoginServiceImpl implements UserLoginService {
@Autowired
private SsbInvoker ssbInvoker;
@Autowired
private UserRealNameTwoVerificationService userRealNameTwoVerificationService;
@Override
public UserDetailDTO loadUser(LoginUsernamePasswordDTO loginUsernamePasswordDTO) {
// 校验参数
@@ -448,4 +454,35 @@ public class UserLoginServiceImpl implements UserLoginService {
}
@Override
public UserDetailDTO loadUser(LoginUCardDTO loginUCardDTO) {
String cardNo = loginUCardDTO.getCardNo();
String realName = loginUCardDTO.getRealName();
// 查询用户
List<UserDetailDTO> userDetailDTOs = userMapper.listUserDetailDTO(UserDetailQueryDTO.builder()
.cardNo(cardNo).userType(UserTypeEnum.PERSON.getType()).build());
Assert.isTrue(userDetailDTOs.size() < 2, new BadLoginException("用户数据异常,请联系管理员!"));
if (userDetailDTOs.size() == 1) {
UserDetailDTO userDetailDTO = userDetailDTOs.get(0);
if (userDetailDTO.getAuthenticated()) {
Assert.isTrue(realName.equals(userDetailDTO.getRealName()),
new BadLoginException("用户实名信息错误"));
return userDetailDTO;
}
}
// 未实名,调第三方实名校验
Long userId = null;
try {
userId = userRealNameTwoVerificationService
.superAuth(SuperRealNameTwoAuthDTO.builder().realName(realName).cardNo(cardNo).build());
} catch (Exception e) {
throw new BadLoginException(e.getMessage(), e);
}
UserDetailDTO userDetailDTO = this.findUserDetailDTO(
UserDetailQueryDTO.builder().userId(userId).userType(UserTypeEnum.PERSON.getType())
.build());
dealActiveStatus(userDetailDTO);
return userDetailDTO;
}
}

View File

@@ -107,7 +107,7 @@
and deleted = 0 and status != 3
and (password is null or password = '')
</update>
<select id="listUserDetailDTO" resultType="com.newyetai.usercenter.user.login.dto.UserDetailDTO">
<!-- 非个人用户时,需要默认一个登录机构 -->
SELECT
@@ -119,20 +119,23 @@
<if test="param.username!=null and param.username!=''">
and u.username = #{param.username}
</if>
<if test="param.cardNo!=null and param.cardNo!=''">
and u.card_no = #{param.cardNo}
</if>
<if test="param.phone!=null and param.phone!=''">
and u.phone = #{param.phone}
<if test="param.userType == 2">
<!-- 个人用户(绑定用户|不存在绑定用户) -->
and (
u.phone_binding = 1
or not exists (select 1
from uc_user pu
where pu.phone = #{param.phone}
or not exists (select 1
from uc_user pu
where pu.phone = #{param.phone}
and pu.phone_binding = 1
and pu.type = 2
and pu.deleted = 0)
)
</if>
</if>
<if test="param.userId!=null">
@@ -153,8 +156,8 @@
</if>
<if test="param.orgId != null">
and exists (
select *
from uc_org_user ou
select *
from uc_org_user ou
where ou.user_id = u.id
and ou.org_id = #{param.orgId}
and ou.deleted = 0