认证业务
1、手机短信验证码防刷校验
将第一次生成的验证码存入redis,并设置过期时间,当二次请求的时候从redis获取验证码,如果存在则校验过期时间
@ResponseBody
@GetMapping("/sms/sendcode")
public R sendCode(@RequestParam("phone") String phone) {
String redisCode = redisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone);
if (!StringUtils.isEmpty(redisCode)) {
long l = Long.parseLong(redisCode.split("_")[1]);
//校验手机验证码的过期时间是否小于1分钟
if (System.currentTimeMillis() - l < 60000) {
//60秒内不能发送
return R.error(BizCodeEnume.SMS_CODE_EXCEPTION.getCode(), BizCodeEnume.SMS_CODE_EXCEPTION.getMsg());
}
}
//TODO 1.接口防刷
//2、验证码的再次校验。redis。存Key-phone,value-code sms:
String code = UUID.randomUUID().toString().substring(0, 5) + "_" + System.currentTimeMillis();
//redis缓存手机验证码,防止同一个phone在60秒内再次发送验证码
redisTemplate.opsForValue().set(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone, code, 10, TimeUnit.MINUTES);
thirdPartyFeignService.sendCode(phone, code);
return R.ok();
}
2、自定义异常
新建异常类
public class UsernameExistException extends RuntimeException {
public UsernameExistException() {
super("用户名已存在");
}
}
在需要的地方主动抛出异常
@Override
public void checkUsernameUnique(String username) throws UsernameExistException {
MemberDao memberDao = this.baseMapper;
Integer mobile = memberDao.selectCount(new QueryWrapper<MemberEntity>().eq("username", username));
if (mobile > 0) {
throw new UsernameExistException();
}
}
3、MD5&MD5盐值加密
- MD5
- Message Digest algorithm 5,信息摘要算法
- 压缩性:任意长度的数据,算出的MD5值长度都是固定的。
- 容易计算:从原数据计算出MD5值很容易。
- 抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
- 强抗碰撞:想找到两个不同的数据,使它们具有相同的MD5值,是非常困难的。
- 加盐:
- 通过生成随机数与MD5生成字符串进行组合
- 数据库同时存储MD5值与salt值。验证正确性时使用salt进行MD5即可
//密码要加密存储
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encode = passwordEncoder.encode(vo.getPassword());
entity.setPassword(encode);
//登录时密码校验
passwordEncoder.matches("用户输入的明文密码","数据库的密文密码");
BCryptPasswordEncoder自动使用盐值加密,无需再进行手动加盐
4、社交登录
QQ、微博、github 等网站的用户量非常大,别的网站为了
简化自我网站的登陆与注册逻辑,引入社交登陆功能;
步骤:
1)、用户点击 QQ 按钮
2)、引导跳转到 QQ 授权页
3)、用户主动点击授权,跳回之前网页。
1、OAuth2.0
- OAuth: OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储
在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们
数据的所有内容。
- OAuth2.0:对于用户相关的 OpenAPI(例如获取用户信息,动态同步,照片,日志,分
享等),为了保护用户数据的安全和隐私,第三方网站访问用户数据前都需要显式的向
用户征求授权。
(A)用户点击社交登录以后,跳转至社交登录服务器(如QQ、微博等)进行授权(认证获取授权码直接从前端跳转至授权页面,无需经过后端处理)
(B)用户验证通过同意给予客户端授权后,跳转至后端处理接口(根据成功授权后得到授权码发送至后端接口处理)。
(C)客户端使用上一步获得的授权,向认证服务器申请令牌(后端接口根据授权码向认证服务器请求获取访问令牌)。
(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
(E)客户端使用令牌,向资源服务器申请获取资源。
(F)资源服务器确认令牌无误,同意向客户端开放资源。
微博Oauth2文档:https://open.weibo.com/wiki/%E6%8E%88%E6%9D%83%E6%9C%BA%E5%88%B6%E8%AF%B4%E6%98%8E
@GetMapping("oauth2.0/weibo/success")
public String weibo(@RequestParam("code") String code, HttpSession session) throws Exception {
Map<String,String> map = new HashMap<>();
map.put("client_id","");
map.put("client_secret","");
map.put("grant_type","authorization_code");
map.put("redirect_uri","http://xxx.com/success");
map.put("code",code);
//1、根据code换取accessToken;
HttpResponse response = HttpUtils.doPost("https://api.weibo.com", "/oauth2/access_token", "post", null, null, map);
//2、处理
if (response.getStatusLine().getStatusCode()==200){
//获取到accessToken
String json = EntityUtils.toString(response.getEntity());
SocialUser socialUser = JSON.parseObject(json, SocialUser.class);
//知道当前是哪个社交用户
//1)、当前用户如果第一次进网站,自动注册进来(为当前社交用户生成一个会员信息账号,以后这个社交账号就对应指定的会员)
R oauthlogin = memberFeignService.oauthlogin(socialUser); //调用feign处理用户是否注册过
if (oauthlogin.getCode()==0){
MemberRespVo data = oauthlogin.getData(new TypeReference<MemberRespVo>() {});
System.out.println("登录成功:用户信息"+data);
log.info("登录成功:用户:{},",data);
//1、第一次使用session;命令浏览器保存卡号。JSESSIONID这个cookie;
//以后浏览器访问那个网站就会带上这个网站的cookie
//子域之间;aa.com auth.aa.com order.aa.com
//发卡的时候(指定域名为父域名),即使是子域系统发的卡,也能让父域直接使用。
session.setAttribute("loginUser",data);
//2、登录成功跳至首页
return "redirect:http://aa.com";
}else {
return "redirect:http://auth.aa.com/login.html";
}
}else {
return "redirect:http://auth.aa.com/login.html";
}
}
5、SSO(单点登录)
Single Sign On 一处登陆、处处可用
单点登录业务介绍
解决 :
- 用户身份信息独立管理,更好的分布式管理。
- 可以自己扩展安全策略
- 跨域不是问题
缺点:
认证服务器访问压力较大。
几个基本概念
什么是跨域 Web SSO。
域名通过“.”号切分后,从右往左看,不包含“.”的是顶级域名,包含一个“.”的是一级域名,
包含两个“.”的是二级域名,以此类推。
例如对网址 http://www.cnblogs.com/baibaomen,域名部分是 www.cnblogs.com。
用“.”拆分后从右往左看:
cookie.setDomain(“.cnblogs.com”);//最多设置到本域的一级域名这里
cookie.setDomain(“.baidu.com”);//最多设置到本域的一级域名这里
”com”不包含“.”,是顶级域名; “cnblogs.com”包含一个“.”,是一级域名;
www.cnblogs.com 包含两个“.”,是二级域名。
blog.cnblogs.com
news.cnblogs.com跨域 Web SSO 指的是针对 Web 站点,各级域名不同都能处理的单点登录方案。
单点登录简单过程
• /xxl-sso-server 登录服务器 8080 ssoserver.com
• /xxl-sso-web-sample-springboot 项目1 8081 client1.com
• /xxl-sso-web-sample-springboot 项目2 8082 client2.com
核心:三个系统即使域名不一样,想办法给三个系统同步同一个用户的票据;
1)、中央认证服务器;ssoserver.com
2)、client1系统,想要登录则去ssoserver.com登录,登录成功后生成token存至ssoserver.com跳转回来(携带token)
3)、client1系统获得token后再将请求至ssoserver.com获取用户信息保存至client1系统的session中,其他client2系统访问时则存入自己的session中
3)、只要有一个登录,其他都不用登录(其他系统登录时去ssoserver.com查询是否存在token,若存在的返回token,再请求获取用户信息)
4)、全系统统一一个sso-sessionid;所有系统可能域名都不相同
总结
- 在 Web 应用中,别再把 JWT 当做 session 使用,绝大多数情况下,传统的
cookie-session 机制工作得更好
- JWT 适合一次性的命令认证,颁发一个有效期极短的 JWT,即使暴露了危险也
很小,由于每次操作都会生成新的 JWT,因此也没必要保存 JWT,真正实现无
状态。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?