Browse Source

添加手机号验证码登录功能

master
ccsens_zhengzhichuan 2 months ago
parent
commit
783b586b62
  1. 34
      research-admin/src/main/java/com/research/web/controller/system/SysLoginController.java
  2. 8
      research-admin/src/main/java/com/research/web/controller/tenant/TmsLoginController.java
  3. 12
      research-framework/src/main/java/com/research/framework/config/SecurityConfig.java
  4. 38
      research-framework/src/main/java/com/research/framework/security/provider/AdminPhoneAuthenticationProvider.java
  5. 81
      research-framework/src/main/java/com/research/framework/security/token/AdminPhoneAuthenticationToken.java
  6. 33
      research-framework/src/main/java/com/research/framework/web/service/UserDetailsServiceImpl.java
  7. 74
      research-framework/src/main/java/com/research/framework/web/service/WebTmsLoginService.java
  8. 33
      research-system/src/main/java/com/research/system/domain/vo/TmsLoginUserVo.java
  9. 8
      research-system/src/main/java/com/research/system/mapper/SysUserMapper.java
  10. 7
      research-system/src/main/java/com/research/system/service/ISysUserService.java
  11. 5
      research-system/src/main/java/com/research/system/service/impl/SysUserServiceImpl.java
  12. 34
      research-system/src/main/resources/mapper/dao/KtsGroupDao.xml
  13. 5
      research-system/src/main/resources/mapper/system/SysUserMapper.xml

34
research-admin/src/main/java/com/research/web/controller/system/SysLoginController.java

@ -3,9 +3,19 @@ package com.research.web.controller.system;
import java.util.List;
import java.util.Set;
import com.research.common.annotation.Anonymous;
import com.research.common.core.domain.JsonResponse;
import com.research.framework.web.service.WebTmsLoginService;
import com.research.system.domain.dto.SmsDto;
import com.research.system.domain.vo.SmsVo;
import com.research.system.domain.vo.TmsLoginUserVo;
import com.research.system.service.SessionService;
import com.research.system.service.TmsLoginService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import liquibase.pro.packaged.E;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@ -45,7 +55,10 @@ public class SysLoginController
private TokenService tokenService;
@Resource
private SessionService sessionService;
@Resource
private WebTmsLoginService tmsTenantUserService;
@Resource
private TmsLoginService dmsLoginService;
/**
* 登录方法
*
@ -118,4 +131,23 @@ public class SysLoginController
List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
return AjaxResult.success(menuService.buildMenus(menus));
}
@ApiOperation("获取手机验证码")
@GetMapping("/getSmsCode")
@Anonymous
public JsonResponse<SmsVo.GetSmsCodeVo> getSmsCode(@ApiParam @Validated SmsDto.GetSmsCode dto) {
return JsonResponse.ok(dmsLoginService.sendSmsCode(dto.getPhone()));
}
@ApiOperation("手机号登录")
@PostMapping("/phoneLogin")
@Anonymous
public AjaxResult loginByPhone(@ApiParam @Validated @RequestBody TmsLoginUserVo.PhoneLogin dto) {
AjaxResult ajax = AjaxResult.success();
// 生成令牌
String token = tmsTenantUserService.loginByPhone(dto.getPhone(), dto.getSmsCode()).getToken();
ajax.put(Constants.TOKEN, token);
return ajax;
}
}

8
research-admin/src/main/java/com/research/web/controller/tenant/TmsLoginController.java

@ -172,4 +172,12 @@ public class TmsLoginController {
public JsonResponse<SmsVo.GetSmsCodeVo> getSmsCode(@ApiParam @Validated SmsDto.GetSmsCode dto) {
return JsonResponse.ok(dmsLoginService.sendSmsCode(dto.getPhone()));
}
@ApiOperation("手机号登录")
@PostMapping("/phoneLogin")
@Anonymous
public JsonResponse<TmsLoginUserVo.Result> loginByPhone(@ApiParam @Validated @RequestBody TmsLoginUserVo.PhoneLogin dto) {
// 生成令牌
return JsonResponse.ok(tmsTenantUserService.loginByPhone(dto.getPhone(), dto.getSmsCode()));
}
}

12
research-framework/src/main/java/com/research/framework/config/SecurityConfig.java

@ -1,5 +1,6 @@
package com.research.framework.config;
import com.research.framework.security.provider.AdminPhoneAuthenticationProvider;
import com.research.framework.security.provider.DmsUserAuthenticationProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
@ -76,7 +77,7 @@ public class SecurityConfig
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());
return new ProviderManager(daoAuthenticationProvider, dmsUserAuthenticationProvider());
return new ProviderManager(daoAuthenticationProvider, dmsUserAuthenticationProvider(), adminPhoneAuthenticationProvider());
}
/**
@ -143,6 +144,11 @@ public class SecurityConfig
return new DmsUserAuthenticationProvider();
}
@Bean
public AdminPhoneAuthenticationProvider adminPhoneAuthenticationProvider(){
return new AdminPhoneAuthenticationProvider();
}
// @Override
// protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// //填报端认证
@ -155,7 +161,9 @@ public class SecurityConfig
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ... 其他配置
.authenticationProvider(dmsUserAuthenticationProvider()); // 注入自定义 Provider
.authenticationProvider(dmsUserAuthenticationProvider())
.authenticationProvider(adminPhoneAuthenticationProvider()); // 注入自定义 Provider
return http.build();
}
}

38
research-framework/src/main/java/com/research/framework/security/provider/AdminPhoneAuthenticationProvider.java

@ -0,0 +1,38 @@
package com.research.framework.security.provider;
import com.research.framework.security.token.AdminPhoneAuthenticationToken;
import com.research.framework.web.service.UserDetailsServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import javax.annotation.Resource;
@Slf4j
public class AdminPhoneAuthenticationProvider implements AuthenticationProvider {
@Resource
private UserDetailsServiceImpl userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
AdminPhoneAuthenticationToken authenticationToken = (AdminPhoneAuthenticationToken) authentication;
String phone = (String) authentication.getPrincipal();
String smsCode = (String) authentication.getCredentials();
//通过openId获取用户
log.info("通过手机号获取用户:{}", phone);
UserDetails userDetails = userDetailsService.loadUserByPhone(phone);
//返回用户信息
AdminPhoneAuthenticationToken result = new AdminPhoneAuthenticationToken(userDetails, smsCode, null);
result.setDetails(authentication.getDetails());
return result;
}
@Override
public boolean supports(Class<?> authentication) {
return (AdminPhoneAuthenticationToken.class.isAssignableFrom(authentication));
}
}

81
research-framework/src/main/java/com/research/framework/security/token/AdminPhoneAuthenticationToken.java

@ -0,0 +1,81 @@
package com.research.framework.security.token;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import java.util.Collection;
/**
* @author zhangsan
* @date 2022-08-11 21:15
* @description TODO
*/
public class AdminPhoneAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final Object principal;
private String credentials;
// private final Object detail;
/**
* 准备登录时调用
* 此构造函数用来初始化未授信凭据.
*
* @param principal
* @param credentials
*/
public AdminPhoneAuthenticationToken(Object principal, String credentials, Object detail) {
super(null);
this.principal = principal;
this.credentials = credentials;
// this.detail = detail;
setAuthenticated(false);
}
/**
* 登录成功时调用
* 此构函数用来初始化已授信凭据.
* @param principal
* @param authorities
*/
public AdminPhoneAuthenticationToken(Object principal, String credentials, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
// this.detail = null;
// super.setAuthenticated(true);
// super.setAuthenticated(false);
super.setAuthenticated(true);
}
@Override
public Object getPrincipal() {
return principal;
}
@Override
public Object getCredentials() {
return credentials;
}
// @Override
// public Object getDetails() {
// return detail;
// }
@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();
credentials = null;
}
}

33
research-framework/src/main/java/com/research/framework/web/service/UserDetailsServiceImpl.java

@ -143,4 +143,37 @@ public class UserDetailsServiceImpl implements UserDetailsService
DynamicDataSourceContextHolder.clearDataSourceType();
return new LoginUser(user.getDmsUserId(), user.getHospitalId(), sysUser, menuPermission, user.getTenantId() ,user.getScoreId());
}
public UserDetails createLoginUser(SysUser user) {
return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
}
public UserDetails loadUserByPhone(String phone) {
SysUser user = userService.selectUserByUserName(phone);
if (StringUtils.isNull(user))
{
log.info("登录用户:{} 不存在.", phone);
throw new ServiceException(MessageUtils.message("user.not.exists"));
}
else if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
{
log.info("登录用户:{} 已被删除.", phone);
throw new ServiceException(MessageUtils.message("user.password.delete"));
}
else if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
{
log.info("登录用户:{} 已被停用.", phone);
throw new ServiceException(MessageUtils.message("user.blocked"));
}
// passwordService.validate(user);
List<PrjProjInfo> prjProjInfos = prjProjInfoMapper.selectByExample(new PrjProjInfoExample());
Long tenantId = 0L;
if (CollUtil.isNotEmpty(prjProjInfos)) {
tenantId = prjProjInfos.get(0).getTenantId();
}
return createLoginUser(user, tenantId);
}
}

74
research-framework/src/main/java/com/research/framework/web/service/WebTmsLoginService.java

@ -3,10 +3,12 @@ package com.research.framework.web.service;
import com.research.common.constant.CacheConstants;
import com.research.common.constant.Constants;
import com.research.common.constant.UserConstants;
import com.research.common.core.domain.PhoneCode;
import com.research.common.core.domain.entity.SysUser;
import com.research.common.core.domain.model.LoginUser;
import com.research.common.core.redis.RedisCache;
import com.research.common.exception.ServiceException;
import com.research.common.exception.base.BaseException;
import com.research.common.exception.user.*;
import com.research.common.utils.DateUtils;
import com.research.common.utils.MessageUtils;
@ -15,9 +17,13 @@ import com.research.common.utils.ip.IpUtils;
import com.research.framework.manager.AsyncManager;
import com.research.framework.manager.factory.AsyncFactory;
import com.research.framework.security.context.AuthenticationContextHolder;
import com.research.framework.security.token.AdminPhoneAuthenticationToken;
import com.research.framework.security.token.DmsUserAuthenticationToken;
import com.research.system.domain.vo.TmsLoginUserVo;
import com.research.system.service.ISysConfigService;
import com.research.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.constant.ErrorConstant;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@ -31,6 +37,7 @@ import javax.annotation.Resource;
*
* @author research
*/
@Slf4j
@Component
public class WebTmsLoginService
{
@ -258,6 +265,73 @@ public class WebTmsLoginService
}
}
private String getPhoneCodeKey(String phone) {
return CacheConstants.PHONE_CODE_KEY + phone;
}
/**
* 手机号登录
*
* @param phone 手机号
* @param smsCode 验证码
* @param phone
* @return
*/
public TmsLoginUserVo.Result loginByPhone(String phone, String smsCode) {
log.info("phone:{}, {}", phone, smsCode);
//手机验证码是否正确
PhoneCode phoneCode = redisCache.getCacheObject(getPhoneCodeKey(phone));
if (phoneCode == null || !phoneCode.getCode().equals(smsCode)) {
throw new BaseException("验证码错误");
}
redisCache.deleteObject(getPhoneCodeKey(smsCode));
//根据手机号查询用户
SysUser sysUser = userService.selectUserByPhone(phone);
if (sysUser == null) {
throw new BaseException("手机号未找到");
}
Authentication authentication = null;
try {
AdminPhoneAuthenticationToken adminPhoneAuthenticationToken = new AdminPhoneAuthenticationToken(phone, smsCode, null);
AuthenticationContextHolder.setContext(adminPhoneAuthenticationToken);
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager.authenticate(adminPhoneAuthenticationToken);
} catch (Exception e) {
if (e instanceof BadCredentialsException) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(phone, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
} else {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(phone, Constants.LOGIN_FAIL, e.getMessage()));
throw new ServiceException(e.getMessage());
}
} finally {
AuthenticationContextHolder.clearContext();
}
//用户不存在 新增
if (authentication == null || authentication.getPrincipal() == null) {
log.info("登录失败authentication:{}", authentication);
throw new BaseException("登录失败,请联系系统管理人员");
}
Object principal = authentication.getPrincipal();
log.info("登录手机号:{}", principal);
AsyncManager.me().execute(AsyncFactory.recordLogininfor(sysUser.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
recordLoginInfo(loginUser.getUserId());
TmsLoginUserVo.Result result = new TmsLoginUserVo.Result();
// 生成token
result.setToken(tokenService.createToken(loginUser));
result.setId(loginUser.getUserId());
if (loginUser.getUser() != null) {
result.setPhone(loginUser.getUser().getPhonenumber());
// result.setTemp(sysUser.getTemp());
}
return result;
}
/**
* 记录登录信息
*

33
research-system/src/main/java/com/research/system/domain/vo/TmsLoginUserVo.java

@ -1,9 +1,13 @@
package com.research.system.domain.vo;
import cn.hutool.core.util.StrUtil;
import com.research.common.core.domain.entity.SysRole;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import java.util.Date;
import java.util.List;
@ -15,6 +19,17 @@ import java.util.List;
*/
public class TmsLoginUserVo {
@Data
@ApiModel("DTO-小程序登录")
public static class PhoneLogin {
@ApiModelProperty("手机号")
@NotBlank(message = "phone不能为空")
@Pattern(regexp = "(?:0|86|\\+86)?1[3-9]\\d{9}", message = "phone格式不正确")
private String phone;
@ApiModelProperty("验证码")
private String smsCode;
}
@Data
public static class Query {
@ -89,4 +104,22 @@ public class TmsLoginUserVo {
List<SysRole> roles;
}
@Data
public static class Result{
private String account;
private String phone;
private String token;
private Long id;
private Byte temp;
public String getAccount(){
if (StrUtil.isBlank(phone)) {
return null;
}
return StrUtil.hide(phone, 3, 7);
}
}
}

8
research-system/src/main/java/com/research/system/mapper/SysUserMapper.java

@ -124,4 +124,12 @@ public interface SysUserMapper
* @return 结果
*/
public SysUser checkEmailUnique(String email);
/**
* 通过用户名查询用户
*
* @param userName 用户名
* @return 用户对象信息
*/
public SysUser selectUserByPhone(String phone);
}

7
research-system/src/main/java/com/research/system/service/ISysUserService.java

@ -203,4 +203,11 @@ public interface ISysUserService
* @return 结果
*/
public String importUser(List<SysUser> userList, Boolean isUpdateSupport, String operName);
/**
* 通过用户ID查询用户
*
* @param userId 用户ID
* @return 用户对象信息
*/
public SysUser selectUserByPhone(String phone);
}

5
research-system/src/main/java/com/research/system/service/impl/SysUserServiceImpl.java

@ -547,4 +547,9 @@ public class SysUserServiceImpl implements ISysUserService
}
return successMsg.toString();
}
@Override
public SysUser selectUserByPhone(String phone) {
return userMapper.selectUserByPhone(phone);
}
}

34
research-system/src/main/resources/mapper/dao/KtsGroupDao.xml

@ -207,9 +207,9 @@
<select id="queryZdXlStatistics" resultType="com.research.system.domain.vo.GroupVO$Statistics$Xl">
select o.id as orgId,
o.org_name as orgName,
SUM(CASE WHEN m.qualification = '5' THEN 1 ELSE 0 END) AS ss,
SUM(CASE WHEN m.qualification = '6' THEN 1 ELSE 0 END) AS bs,
SUM(CASE WHEN m.qualification = '7' THEN 1 ELSE 0 END) AS bsh
SUM(CASE WHEN m.reading_status = '0' THEN 1 ELSE 0 END) AS ss,
SUM(CASE WHEN m.reading_status = '1' THEN 1 ELSE 0 END) AS bs,
SUM(CASE WHEN m.reading_status = '2' THEN 1 ELSE 0 END) AS bsh
from prj_proj_org o
left join
kts_kt_group g on g.proj_org_id = o.id and g.del_flag = 0
@ -221,9 +221,9 @@
<select id="queryByXlStatistics" resultType="com.research.system.domain.vo.GroupVO$Statistics$Xl">
select o.id as orgId,
o.org_name as orgName,
SUM(CASE WHEN m.qualification = '5' THEN 1 ELSE 0 END) AS ss,
SUM(CASE WHEN m.qualification = '6' THEN 1 ELSE 0 END) AS bs,
SUM(CASE WHEN m.qualification = '7' THEN 1 ELSE 0 END) AS bsh
SUM(CASE WHEN m.reading_status = '0' THEN 1 ELSE 0 END) AS ss,
SUM(CASE WHEN m.reading_status = '1' THEN 1 ELSE 0 END) AS bs,
SUM(CASE WHEN m.reading_status = '2' THEN 1 ELSE 0 END) AS bsh
from prj_proj_org o
left join
kts_kt_group g on g.proj_org_id = o.id and g.del_flag = 0
@ -327,32 +327,30 @@
<resultMap id="ResultMap" type="com.research.system.domain.vo.GroupVO$GroupStatistics">
<id property="orgId" column="orgId"/>
<result property="orgName" column="orgName"/>
<collection property="groupList" ofType="com.research.system.domain.vo.GroupVO$Statistics1">
<id property="groupId" column="groupId"/>
<result property="groupName" column="groupName"/>
<result property="num" column="num"/>
</collection>
<!-- <collection property="groupList" ofType="com.research.system.domain.vo.GroupVO$Statistics1">-->
<!-- <id property="groupId" column="groupId"/>-->
<!-- <result property="groupName" column="groupName"/>-->
<!-- <result property="num" column="num"/>-->
<!-- </collection>-->
</resultMap>
<select id="statistics1" resultMap="ResultMap">
SELECT
p.id AS orgId,
p.org_name AS orgName,
k1.id AS groupId,
k1.kt_group_name AS groupName,
IF(COUNT(k2.id)>0, COUNT(k2.id), null) AS num
-- k1.id AS groupId,
-- k1.kt_group_name AS groupName,
IF(COUNT(k1.id)>0, COUNT(k1.id), null) AS num
FROM
prj_proj_org p
LEFT JOIN
kts_kt_group k1 ON p.id = k1.proj_org_id AND k1.parent_kt_id = 0 AND k1.del_flag = 0
LEFT JOIN
kts_kt_group k2 ON k1.id = k2.parent_kt_id AND k2.del_flag = 0
WHERE
p.del_flag = 0
GROUP BY
p.id, k1.id
p.id
ORDER BY
p.id, k1.id
p.id
</select>
<select id="zdStatistics" resultType="com.research.system.domain.vo.GroupVO$ZdStatistics">

5
research-system/src/main/resources/mapper/system/SysUserMapper.xml

@ -141,6 +141,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<select id="checkEmailUnique" parameterType="String" resultMap="SysUserResult">
select user_id, email from sys_user where email = #{email} and del_flag = '0' limit 1
</select>
<select id="selectUserByPhone" parameterType="String" resultMap="SysUserResult">
<include refid="selectUserVo"/>
where u.phonenumber = #{phone} and u.del_flag = '0'
</select>
<insert id="insertUser" parameterType="SysUser" useGeneratedKeys="true" keyProperty="userId">
insert into sys_user(

Loading…
Cancel
Save