在用戶登錄的時候,需要做安全校驗,加驗證碼是普遍的做法,分析一下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);
}
}
自己一些小總結,還在學習階段