ZQN
2025-05-19 0e12e4ab45db6768a0f45d8952f78b0ae9190723
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
package com.project.framework.web.service;
 
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.project.common.constant.AliyunSmsConstants;
import com.project.common.constant.CacheConstants;
import com.project.common.constant.Constants;
import com.project.common.core.domain.entity.SysUser;
import com.project.common.core.domain.model.LoginUser;
import com.project.common.core.redis.RedisCache;
import com.project.common.exception.ServiceException;
import com.project.common.exception.base.BaseException;
import com.project.common.exception.user.CaptchaException;
import com.project.common.exception.user.CaptchaExpireException;
import com.project.common.exception.user.UserPasswordNotMatchException;
import com.project.common.utils.DateUtils;
import com.project.common.utils.MessageUtils;
import com.project.common.utils.ServletUtils;
import com.project.common.utils.StringUtils;
import com.project.common.utils.ip.IpUtils;
import com.project.framework.manager.AsyncManager;
import com.project.framework.manager.factory.AsyncFactory;
import com.project.framework.security.context.AuthenticationContextHolder;
import com.project.system.domain.bo.editBo.UserPhoneLoginBo;
import com.project.system.mapper.SysUserMapper;
import com.project.system.service.ISysConfigService;
import com.project.system.service.ISysUserService;
import com.project.system.sms.YPSmsApi;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
 
import java.util.Random;
import java.util.concurrent.TimeUnit;
 
/**
 * 登录校验方法
 *
 * @author project
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class SysLoginService
{
    private final TokenService tokenService;
    private final AuthenticationManager authenticationManager;
    private final RedisCache redisCache;
    private final ISysUserService userService;
    private final SysUserMapper userMapper;
    private final ISysConfigService configService;
    private final YPSmsApi smsApi;
 
 
    /**
     * 登录验证
     *
     * @param username 用户名
     * @param password 密码
     * @param code 验证码
     * @param uuid 唯一标识
     * @return 结果
     */
    public String login(String username, String password, String code, String uuid)
    {
        boolean captchaEnabled = configService.selectCaptchaEnabled();
        // 验证码开关
        if (captchaEnabled)
        {
            validateCaptcha(username, code, uuid);
        }
        // 用户验证
        Authentication authentication;
        try
        {
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
            AuthenticationContextHolder.setContext(authenticationToken);
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(authenticationToken);
        }
        catch (Exception e)
        {
            if (e instanceof BadCredentialsException)
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                throw new UserPasswordNotMatchException();
            }
            else
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                throw new ServiceException(e.getMessage());
            }
        }
        finally
        {
            AuthenticationContextHolder.clearContext();
        }
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        recordLoginInfo(loginUser.getUserId());
 
        // 生成token
        return tokenService.createToken(loginUser);
    }
 
    /**
     * 新注册获取验证码
     * @param phone 手机
     * @return  验证码
     */
    public Boolean getVerifyCodeNew(String phone) {
        // 生成4位随机数
        String code = "";
        Random ran = new Random();
        int randomNum = ran.nextInt(10000);
        code = String.format("%04d", randomNum);
        log.info("手机号:"+phone+"->验证码:"+code);
        boolean send = sendYp(phone, code);
        if (send){
            redisCache.setCacheObject(getCacheKey(phone), code, Constants.PHONE_EXPIRATION, TimeUnit.MINUTES);
            return true;
        }
        redisCache.setCacheObject(getCacheKey(phone), code, Constants.PHONE_EXPIRATION, TimeUnit.MINUTES);
        return false;
    }
 
    /**
     * 获取验证码
     * @param phone 手机号
     * @return  验证码
     */
    public Boolean getVerifyCode(String phone)
    {
        SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhonenumber,phone));
        if (user==null){
            throw new BaseException("您手机号尚未注册!");
        }
        return getVerifyCodeNew(phone);
    }
 
    /**
     * 云片验证码
     * @param phone 手机
     * @param code  验证码
     * @return  结果
     */
    private boolean sendYp(String phone, String code)
    {
        String result = smsApi.sendSms(phone, StringUtils.format(YPSmsApi.CODE_TMP, code, Constants.PHONE_EXPIRATION));
        if (result.contains("\"code\":0,\"msg\":\"OK\"")){
            log.info("发送成功 ->验证码:"+code);
            return true;
        }
        return false;
    }
 
    /**
     * 阿里验证码
     * @param phone 手机
     * @param code  验证码
     * @return  结果
     */
    private boolean sendAl(String phone, String code )
    {
        DefaultProfile profile = DefaultProfile.getProfile("cn-beijing", AliyunSmsConstants.SMS_APPID, AliyunSmsConstants.SMS_SECRET);
        IAcsClient client = new DefaultAcsClient(profile);
        CommonRequest request = new CommonRequest();
        request.setMethod(MethodType.POST);
        request.setDomain("dysmsapi.aliyuncs.com");
        request.setVersion("2017-05-25");
        request.setAction("SendSms");
        request.putQueryParameter("PhoneNumbers", phone);
        request.putQueryParameter("SignName", "盛商珠宝");
        request.putQueryParameter("TemplateCode", "SMS_460945884");
        request.putQueryParameter("TemplateParam", "{code:" + code + "}");
        try {
            CommonResponse response = client.getCommonResponse(request);
            JSONObject jsonObject = JSON.parseObject(response.getData());
            if ("OK".equals(jsonObject.get("Code"))) {
                log.info("发送成功 ->验证码:"+code);
                return true;
            }
        } catch (ClientException e) {
            e.printStackTrace();
        }
        return false;
    }
 
    /**
     * 验证码登录
     * @param bo    参数
     * @return  结果
     */
    public String phoneLogin(UserPhoneLoginBo bo)
    {
        String phone = bo.getPhone();
//        Boolean verified = verifyPhone(phone, bo.getCode());
//        if (!verified){
//            throw new BaseException("手机号验证码校验失败!");
//        }
        SysUser user = null;
        if ("01".equals(bo.getUserType())){
            user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
                    .eq(SysUser::getPhonenumber,bo.getPhone())
                    .and(wrapper->wrapper.eq(SysUser::getUserType,"00").or().eq(SysUser::getUserType, "01")));
        } else {
            user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
                    .eq(SysUser::getPhonenumber,bo.getPhone())
                    .eq(SysUser::getUserType,bo.getUserType())
                    );
        }
        if (user==null){
            throw new BaseException("您手机号尚未注册或您选择登录类型有误!");
        }
        if (!"0".equals(user.getStatus())){
            throw new BaseException("您账号已停用或待审批,请联系营商办管理人员!");
        }
        return this.login(user.getUserName(), user.getRecommendUser(), null, null);
    }
 
    /**
     * 校验验证码
     *
     * @param username 用户名
     * @param code 验证码
     * @param uuid 唯一标识
     */
    public void validateCaptcha(String username, String code, String uuid)
    {
        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
        String captcha = redisCache.getCacheObject(verifyKey);
        redisCache.deleteObject(verifyKey);
        if (captcha == null)
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
            throw new CaptchaExpireException();
        }
        if (!code.equalsIgnoreCase(captcha))
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
            throw new CaptchaException();
        }
    }
 
    /**
     * 记录登录信息
     *
     * @param userId 用户ID
     */
    public void recordLoginInfo(Long userId)
    {
        SysUser sysUser = new SysUser();
        sysUser.setUserId(userId);
        sysUser.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
        sysUser.setLoginDate(DateUtils.getNowDate());
        userService.updateUserProfile(sysUser);
    }
 
 
    /**
     * 获取验证码前缀
     * @param phone 手机号
     * @return  验证码
     */
    private String getCacheKey(String phone)
    {
        return CacheConstants.PHONE_CODE_KEY + phone;
    }
 
    /**
     * 校验验证码
     * @param phone 手机号
     * @param code  验证码
     * @return  校验结果
     */
    public Boolean verifyPhone(String phone, String code)
    {
        String key = getCacheKey(phone);
        if (!redisCache.hasKey(key)) {
            return false;
        }
        String redisCode = redisCache.getCacheObject(key);
 
        boolean verify = redisCode.equals(code);
        if (verify){
            redisCache.deleteObject(getCacheKey(phone));
        }
 
        return verify;
    }
 
 
}