在用户登录的时候,需要做安全校验,加验证码是普遍的做法,分析一下ruoyi开源框架的验证码登录实现逻辑
当用户打开登录页面的时候,就要发送请求,去后台生成一个验证码传到前台
1.
@RestController
public class CaptchaController { @Autowired private RedisService redisService; /** * 生成验证码工具类 */ @GetMapping("/creatorCode") public Result creatorCode(HttpServletResponse response) throws IOException { // 生成验证码随机4位字符串 String verifyCode = VerifyCodeUtils.generateVerifyCode(4); // 生成uuid 17ce79633eea49f6a1d4035505078fd8 String uuid = UuIdUtils.createUUID(); //生成的UUID,加上captcha_codes:最为键,当做redis中的key存储code String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; redisCache.setCacheObject(verifyKey, verifyCode, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); // 生成图片 int w = 111, h = 36; ByteArrayOutputStream stream = new ByteArrayOutputStream(); VerifyCodeUtils.outputImage(w, h, stream, verifyCode); try { AjaxResult ajax = AjaxResult.success(); ajax.put("uuid", uuid); ajax.put("img", Base64.encode(stream.toByteArray())); return ajax; } catch (Exception e) { e.printStackTrace(); return AjaxResult.error(e.getMessage()); } finally { stream.close(); } } }
//已生成的Key和对应的Value

2.用户输入账号密码验证码,发送请求到后端进行校验 访问:http://localhost/login
/**
* 登录方法
* @param loginBody 登陆信息
* @return 结果
*/
@PostMapping("/login") public AjaxResult login(@RequestBody LoginBody loginBody) { AjaxResult ajax = AjaxResult.success(); // 生成令牌 String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), loginBody.getUuid()); ajax.put(Constants.TOKEN, token); return ajax; }
SysLoginService 这个类会进行用户信息的校@Component
public class SysLoginService
{
@Autowired
private TokenService tokenService;
@Resource
private AuthenticationManager authenticationManager;
@Autowired
private RedisCache redisCache;
/**
* 登录验证
*
* @param username 用户名
* @param password 密码
* @param captcha 验证码
* @param uuid 唯一标识
* @return 结果
*/
public String login(String username, String password, String code, String uuid)
{
//用户携带过来的:uuid17ce79633eea49f6a1d4035505078fd8
//当登录页面打开的时候,这个uuid和验证码就已经携带发送给页面
System.out.println("用户携带过来的UUID:"+uuid);
//拿到uuid拼接Constants.CAPTCHA_CODE_KEY
String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
//拿到拼接后的数据作为条件查询redis
String captcha = redisCache.getCacheObject(verifyKey);
//redis中删除验证码
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();
}
// 用户验证
Authentication authentication = null;
try
{
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(username, password));
}
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 CustomException(e.getMessage());
}
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
//校验完成后得到 loginUser对象, 基于这个对象生成token,完成登录
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
// 生成token
return tokenService.createToken(loginUser);
}
}
自己一些小总结,还在学习阶段