认证业务

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,真正实现无
状态。

posted @   佳沃  阅读(68)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示