登录问题 - 单点登录(SSO)、微信扫描登录

这篇文章主要说明一下在线教育系统的登录问题
主要介绍 - 单点登录概念、微信扫面登录 (Java 后端)


1. 单点登录

1.1 简介

  • 单点登录 (Single Sign On – SSO)
  • 在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统

1.2 常见的三种单点登录方式

1.2.1 session 广播机制实现

  1. 登录成功后,把用户数据放到 session 里面;
  2. 判断是否登录,从 session 中获取数据,可以获取到登录
session.setAttribute("user",user);
session.getAttribute("user");

1.2.2 使用 cookie + redis 实现

  1. 在项目中任何一个模块进行登录,登录之后,把数据放到两个地方

     redis:在 key 生成唯一随机值(ip、用户id等等);在 value 中存储用户数据
     cookie:把 redis 里面生成的 key 值放到 cookie 里面
    
  2. 访问项目中的其他模块,发送请求带着 cookie 进行发送,获取 redis 值

     把 cookie 获取值,到 redis 进行查询, 根据 key 进行查询,如果查询到数据就是登录
    

1.2.3 使用 token 实现

  1. 在项目中某个模块进行登录,登录之后,按照一定的规则生成字符串,把登录之后的用户信息包含到生成字符串中,返回字符串

     可以把字符串通过 cookie 返回;
     把字符串通过地址栏返回
    
  2. 再去访问项目其他模块,每次访问在地址栏带着生成字符串,可以根据地址栏字符串获取用户信息

1.2.4 JWT

  • token 是按照一定规则生成字符串,包含用户信息
  • 规则是怎么样的,不一定
  • 一般采用官方规则(JWT)

JWT 生成字符串包含三部分:

  1. JWT 头信息;
  2. 有效载荷,包含主体信息(用户信息);
  3. 签名哈希(防伪标志)

2. 登录

  • 实现界面

2.1 普通登录

  • 获取登录手机号、密码
  • 判断手机号、密码是否为空
  • 判断手机号是否正确 – 和数据库中的手机号进行比较
  • 判断密码似乎否正确 – 把输入的密码进行加密,再和数据库密码进行比较
  • 使用 JWT 工具类,生成并返回 token
    //登录的方法
    @Override
    public String login(UcenterMember member) {
        //获取登录手机号和密码
        String mobile = member.getMobile();
        String password = member.getPassword();

        //手机号和密码非空判断
        if(StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) {
            throw new GuliException(20001,"登录失败");
        }

        //判断手机号是否正确
        QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
        wrapper.eq("mobile",mobile);
        UcenterMember mobileMember = baseMapper.selectOne(wrapper);
        //判断查询对象是否为空
        if(mobileMember == null) {//没有这个手机号
            throw new GuliException(20001,"登录失败");
        }

        //判断密码
        //因为存储到数据库密码肯定加密的
        //把输入的密码进行加密,再和数据库密码进行比较
        //加密方式 MD5
        if(!MD5.encrypt(password).equals(mobileMember.getPassword())) {
            throw new GuliException(20001,"登录失败");
        }

        //登录成功
        //生成token字符串,使用jwt工具类
        String jwtToken = JwtUtils.getJwtToken(mobileMember.getId(), mobileMember.getNickname());
        return jwtToken;
    }

2.2 微信扫描登录

2.2.1 OAuth2

  1. OAuth2 是针对特定问题一种解决方案
  2. 主要解决两个问题: 1.开放系统间授权 2.分布式访问问题

开放系统间授权方式:

  • 密码用户名复制、
  • 通用开发者key(万能钥匙)、
  • 办法令牌

分布式访问(单点登录)

1. 登陆成功之后,按照一定的规则生成字符串,字符串包含用户信息
2. 把生成的字符串通过路径传播,或者cookie
3. 后面再发送请求的时候,每次带着字符串进行发送;获取字符串,从字符串中获取用户信息登录

OAuth2解决方案:令牌机制,按照一定规则生成字符串,字符串中包含用户信息

2.2.2 准备工作

简而言之:获得 app_idapp_secret

# 微信开放平台 -- appid
wx.open.app_id=wxed9954c01bb89b47
# 微信开放平台 -- appsecret
wx.open.app_secret=a7482517235173ddb4083788de60b90e
# 微信开放平台 -- 重定向url
wx.open.redirect_url=http://guli.shop/api/ucenter/wx/callback

2.2.3 生成微信扫描二维码

  • 直接请求微信提供固定的地址(向地址后面拼接参数)
  • 返回地址(地址中就是二维码)
    // 生成微信扫描二维码
    @GetMapping("login")
    public String getWxCode() {
        //固定地址,后面拼接参数
//        String url = "https://open.weixin.qq.com/" +
//                "connect/qrconnect?appid="+ ConstantWxUtils.WX_OPEN_APP_ID+"&response_type=code";

        // 微信开放平台授权baseUrl  %s相当于?代表占位符
        String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
                "?appid=%s" +
                "&redirect_uri=%s" +
                "&response_type=code" +
                "&scope=snsapi_login" +
                "&state=%s" +
                "#wechat_redirect";

        //对redirect_url进行URLEncoder编码
        String redirectUrl = ConstantWxUtils.WX_OPEN_REDIRECT_URL;
        try {
            redirectUrl = URLEncoder.encode(redirectUrl, "utf-8");
        }catch(Exception e) {
        }

        //设置%s里面值
        String url = String.format(
                baseUrl,
                ConstantWxUtils.WX_OPEN_APP_ID,
                redirectUrl,
                "kc"
        );

        //重定向到请求微信地址里面
        System.out.println(url);
        return "redirect:"+url;
    }

2.2.4 扫描二维码

步骤

  1. 扫描之后,执行本地的 callback 方法,在跳转时传递过来两个值(state-原样传递)(code-类似于手机验证码);

  2. 拿到第一步的code,请求微信提供固定的地址,获取两个值;

     https://api.weixin.qq.com/sns/oauth2/access_token
    
    1. access_token:访问凭证
    2. openid:每个微信唯一的标识
  3. 拿到 access_token 和 openid ,再去请求一个微信的固定地址,最终可以得到微信扫描人的信息

     https://api.weixin.qq.com/sns/userinfo
    
    @Autowired
    private UcenterMemberService memberService;

    //2 获取扫描人信息,添加数据
    @GetMapping("callback")
    public String callback(String code, String state) {
        try {
            //1 获取code值,临时票据,类似于验证码
            //2 拿着code请求 微信固定的地址,得到两个值 accsess_token 和 openid
            String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
                    "?appid=%s" +
                    "&secret=%s" +
                    "&code=%s" +
                    "&grant_type=authorization_code";
            //拼接三个参数 :id  秘钥 和 code值
            String accessTokenUrl = String.format(
                    baseAccessTokenUrl,
                    ConstantWxUtils.WX_OPEN_APP_ID,
                    ConstantWxUtils.WX_OPEN_APP_SECRET,
                    code
            );
            //请求这个拼接好的地址,得到返回两个值 accsess_token 和 openid
            //使用httpclient发送请求,得到返回结果
            String accessTokenInfo = HttpClientUtils.get(accessTokenUrl);

            //从accessTokenInfo字符串获取出来两个值 accsess_token 和 openid
            //把accessTokenInfo字符串转换map集合,根据map里面key获取对应值
            //使用json转换工具 Gson
            Gson gson = new Gson();
            HashMap mapAccessToken = gson.fromJson(accessTokenInfo, HashMap.class);
            String access_token = (String)mapAccessToken.get("access_token");
            String openid = (String)mapAccessToken.get("openid");

            //把扫描人信息添加数据库里面
            //判断数据表里面是否存在相同微信信息,根据openid判断
            UcenterMember member = memberService.getOpenIdMember(openid);
            if(member == null) {//memeber是空,表没有相同微信数据,进行添加

                //3 拿着得到accsess_token 和 openid,再去请求微信提供固定的地址,获取到扫描人信息
                //访问微信的资源服务器,获取用户信息
                String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
                        "?access_token=%s" +
                        "&openid=%s";
                //拼接两个参数
                String userInfoUrl = String.format(
                        baseUserInfoUrl,
                        access_token,
                        openid
                );
                //发送请求
                String userInfo = HttpClientUtils.get(userInfoUrl);
                System.out.println(userInfo);
                //获取返回userinfo字符串扫描人信息
                HashMap userInfoMap = gson.fromJson(userInfo, HashMap.class);
                String nickname = (String)userInfoMap.get("nickname");//昵称
                String headimgurl = (String)userInfoMap.get("headimgurl");//头像

                member = new UcenterMember();
                member.setOpenid(openid);
                member.setNickname(nickname);
                member.setAvatar(headimgurl);
                memberService.save(member);   // 保存新用户
            }

            //使用jwt根据member对象生成token字符串
            String jwtToken = JwtUtils.getJwtToken(member.getId(), member.getNickname());
            //最后:返回首页面,通过路径传递token字符串
            return "redirect:http://localhost:3000?token="+jwtToken;
        }catch(Exception e) {
            throw new GuliException(20001,"登录失败");
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值