使用java代码实现扫码登录

本文介绍如何使用前端qrcode.js配合Redis实现实时二维码登录验证。前端生成带唯一标识的二维码,扫码后通过Ajax请求判断登录状态,后端存储token并返回,定时器确保状态同步。

使用工具:

        redis+qrcode.js+jquery.js

注意事项:

        扫码手机必须跟项目使用同一网络ip;不然网络ip不同扫码请求访问不到项目服务,我这里是用手机连接电脑热点进行扫码;亲测!!!

整体实现描述:
        前端生成二维码后,同时发起频繁请求来判断二维码的登入状态,用户扫码进行登入后,后端验证登入通过后,把二维码登入状态存入redis,前端接收到成功登入返回值后进行页面跳转。

1.前端登入页面生成二维码
        扫码页面定义一个div容器用来存放生成的二维码,当点击扫码登入的时候使用单机事件,触发一个生成二维码的函数,函数执行qrcode.js的new QRCode(“放二维码的容器”,”二维码内容”)方法来生成一个二维码,二维码内容写入需要访问登入页面的url。同时使用js的setInterval(“执行的方法或代码”,”每隔多少毫秒执行”)方法生成一个定时器,开启定时器用来判断二维码的登入状态,因为考虑到二维码的唯一性,所以需要给二维码设置一个唯一标识,我们可以使用一个Math.random()的随机数方法加上一个当前时间的时间戳,组合成一个字符串,代表二维码的唯一标识,用来保证每个二维码标识的唯一性,把二维码的唯一标识拼接到二维码登入页面的url中,让扫码后登入页面的登入请求使用。

2.前端扫码后登入页面的登入请求
        当我们扫码页面生成二维码之后我们就可以进行扫码登入了,扫描二维码进入到登入页面,因为二维码的唯一性,所以我们要携带着二维码的唯一标识来进行登入请求。因为我们把二维码的唯一标识已经放入二维码的访问url中了,所以我们可以直接获取二维码唯一标识,这里使用window对象的localtion.search的方法来获取请求路径中从问号(?)开始的url路径,也就是我们请求中拼接的二维码唯一标识,截取后获取二维码的唯一标识,发送一个登入请求携带着我们的二维码唯一标识,让后端用来存储二维码的登入状态。

3.后端二维码登入请求的处理
我们后端API接口收到带着二维码标识的登入请求后,就可以进行登入的业务处理了,首先需要验证账号密码是否正确,如果有误直接返回账号或密码验证失败。当账号密码都验证通过后我们对二维码进行处理,我们可以使用redis来存储一个token,用来验证前端登入请求是否登入成功,同时也是用户登入后的token凭证,Key就是二维码的唯一标识,因为是唯一的标识,所以不会造成不同的二维码登入冲突。存储token后就可以返回登入成功的请求了,来告诉扫码页面已经扫码登入成功了。

4.定时器获取后端二维码的登入状态
        我们第一步生成二维码的时候,同时也开启了一个定时器,我们要使用定时器来获取二维码的登入状态,使用定时器调用一个函数,不断发送一个查看二维码登入状态的请求,设置好定时器时间每隔多少毫秒发送请求,因为我们生成的二维码是唯一性的,所以判断二维码登入状态,需要拿着二维码的唯一标识来进行后端的判断,后端收到定时器频繁发来的请求后,进行判断用户是否完成扫码登入,具体实现就是拿着二维码的唯一标识,查询redis是否存有成功登入后存储的token,如果有token证明扫码页面用户已经登入成功了,就可以返回登入成功状态与token凭证到前端了,让前端做出登入成功后的响应与处理。

5.前端登入成功后的响应与处理
        前端定时器执行判断登入状态的请求,一旦接收到后端返回成功登入装态的返回值,就可以进行成功登入的响应处理了,首先获取成功登入的token凭证,使用SessionStore.setItem(“键”,”值”)方法把凭证存储到sessionStore中,让登入后的其他请求使用,使用 定时器.close()方法把判断登入状态的定时器关闭,最后进行登入成功后的页面跳转即可。完成扫码登入!!!
 

 实现代码

1.前端生成二维码并开启定时器:

 //生成二维码函数
        function getQRCode() {
            //随机数+时间戳生成一个二维码唯一标识
            var qrCode = Math.random().toString().substring(2) + new Date().getTime();
            //生成二维码,第一个参数是放二维码的容器,第二个参数是二维码访问地址
            new QRCode(document.getElementById("QRCodeDiv"), "http://192.168.1.113:2222/tm-shop-show/shop-front-end/QRCode.html?QRCode=" + qrCode);
            //创建一个定时器执行函数获取二维码登入状态
            var interval = setInterval(getQRCodeStatus, 1000);
            //定时器函数获取二维码登入状态
            function getQRCodeStatus() {
                $.ajax({
                    url: "http://localhost:7777/user/user/getQRCodeStatus",
                    type: "post",            
                    data: {"qrCode": qrCode},
                    dataType: "json",
                    success: function (result) {
                        if (result.code === 200) {
                            //登入成功把token存入sessionStore中
                            sessionStorage.setItem("token", result.result);
                            //跳转页面
                            $(location).attr("href", "index.html");
                            //关闭定时器
                            clearInterval(interval);
                        }
                    },
                    error: function () {
                        alert("获取登入状态失败");
                    }
                })
            }
        }

3.后端验证登入状态:

 @Override
    public RestReturnModel userLogin(LoginUserEntity loginUserEntity) {
        //获取用户账号密码
        UserEntity user = userMapper.getUser(loginUserEntity.getInputName());
        if (null == user) {
            //如果账号不存在返回账号不存在
            return RestReturnModel.LOGIN_ERROR("账户不存在");
        }
        if (MD5Util.valid(loginUserEntity.getInputPassword(), MD5Util.MD5Upper("password", user.getPassword()))
        ) {
            //如果密码不正确返回密码不正确
            return RestReturnModel.LOGIN_ERROR("密码错误");
        }
        //生成token凭证
        String token = JwtTokenUtil.generateToken(user);
        if (null != loginUserEntity.getQRCode()) {
            //如果是二维码登入存入的key为二维码唯一标识
            redisTemplate.opsForValue().set(loginUserEntity.getQRCode(), token, 30, TimeUnit.MINUTES);
            //返回登入成功
            return RestReturnModel.LOGIN_SUCCESS(token);
        }
        //非二维码登入把token存入到redis
        redisTemplate.opsForValue().set("token_" + user.getUsername(), token, 30, TimeUnit.MINUTES);
        //返回登入成功
        return RestReturnModel.LOGIN_SUCCESS(token);
}

4.后端接收定时器请求返回登入状态:

 @Override
    public RestReturnModel getQRCodeStatus(String qrCode) {
        //拿着二维码唯一标识获取redis中二维码状态
        String token = (String) redisTemplate.opsForValue().get(qrCode);
        //如果不为空则代表二维码登入成功了
        if(null != token){
            //返回成功登入状态
            return RestReturnModel.LOGIN_SUCCESS(token);
        }
        //为空则代表二维码还未登入成功
        return RestReturnModel.LOGIN_ERROR("暂未登入");
    }

<think>好的,我现在需要解决用户关于如何配置Nginx反向代理以正确访问Java后端根路径(/)接口的问题。用户之前的问题是关于Nginx反向代理导致404错误,现在他们想了解如何正确配置根路径的转发。 首先,我需要回顾之前的回答,确保一致性。之前讨论了路径重写、proxy_pass配置、上下文路径等问题。现在用户关注的是根路径,这可能涉及到更简洁的配置,但也可能有隐藏的问题。 接下来,用户提供的引用资料中,引用[1]提到Nginx作为反向代理的基本配置,引用[2]描述了请求处理流程,引用[3]涉及协议转换,引用[4]提到多个location块的配置。这些都需要综合参考。 用户当前的场景是希望将所有根路径请求转发到Java后端,比如访问example.com/ 应该代理到后端服务的/接口。常见的问题可能包括路径被错误地附加、上下文路径不匹配,或者头信息丢失。 我需要考虑几个可能的问题点:当location设置为/时,如何正确配置proxy_pass,确保路径不被修改。同时,如果Java应用有上下文路径,比如Spring Boot的server.servlet.context-path,是否需要重写路径。另外,是否要处理静态资源和动态接口的分流,但用户特别提到根路径接口,可能希望所有请求都转发到后端。 可能的解决方案是使用最简单的proxy_pass配置,但需要注意结尾的斜杠。例如,proxy_pass http://backend/; 不带斜杠可能导致路径附加,而带斜杠则替换整个路径。用户需要根路径转发,所以可能需要不带斜杠,或者根据后端需求调整。 此外,可能需要处理头信息,如Host头,确保后端能正确识别请求。还有可能需要配置WebSocket或其他协议的支持,但用户目前的问题可能集中在基本的HTTP转发。 还要考虑是否存在其他location块干扰,比如如果存在处理静态资源的location /,可能会优先匹配,导致动态请求未被代理。因此,配置顺序和优先级很重要,可能需要使用精确匹配或调整优先级。 总结步骤:检查proxy_pass是否正确,路径是否被重写,头信息是否正确传递,确保没有其他location块冲突,验证后端是否接收正确的请求路径。 然后给出配置示例,并解释每个部分的作用,比如proxy_set_header,rewrite规则是否需要,以及如何测试配置是否正确。可能还需要提醒用户检查后端服务的上下文路径设置,如Spring Boot的context-path是否为/,否则需要路径重写。 最后,生成相关问题,帮助用户深入理解,比如多路径代理、负载均衡或WebSocket配置。</think>#### **Nginx反向代理根路径(/)接口的精准配置指南** --- ### **一、核心问题分析** 根路径代理的核心难点在于**路径完整性保持**与**多服务共存冲突**。根据引用[1][3]的框架特性,需特别注意以下两个关键点: 1. `proxy_pass`的路径拼接逻辑(是否保留结尾斜杠) 2. 后端服务上下文路径(Context Path)的隐式影响 --- ### **二、基础配置模板** #### **场景描述** 将`http://nginx_host/`的所有请求代理至Java后端`http://java_backend:8080/`的根接口 ```nginx server { listen 80; server_name yourdomain.com; location / { proxy_pass http://java_backend:8080; # 关键点:无结尾斜杠 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } ``` #### **配置解析** - **无结尾斜杠**:表示将原始URI完整传递(如请求`/user/list`会代理为`http://java_backend:8080/user/list`)[^1] - **Header传递**:确保后端服务能获取真实客户端IP和原始域名 --- ### **三、典型问题场景与解决方案** #### **1. 后端服务存在上下文路径(如Spring Boot)** **场景**: Java应用配置了`server.servlet.context-path=/app`,需将`/`代理到后端的`/app`路径 **修正配置**: ```nginx location / { rewrite ^/(.*) /app/$1 break; # 路径重写追加上下文 proxy_pass http://java_backend:8080; } ``` **路径变化**: - 请求`/user/list` → 代理为`http://java_backend:8080/app/user/list` --- #### **2. 同时代理静态资源与API接口** **需求**: - `/static/`目录由Nginx直接处理 - 其他请求转发至Java后端 ```nginx server { location /static/ { root /data/www; # 静态资源目录 expires 30d; } location / { proxy_pass http://java_backend:8080; proxy_set_header Host $host; } } ``` **优先级说明**: - Nginx优先匹配更精确的`/static/`路径 - 通用路径`/`最后匹配[^2] --- #### **3. 保留URL编的特殊字符** **问题现象**: 请求`/api%2Fv1/user`被Nginx自动解为`/api/v1/user` **解决方案**: ```nginx location / { proxy_pass http://java_backend:8080; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Original-URI $request_uri; # 传递原始未解URI } ``` **后端处理**: Java服务需从`X-Original-URI`头获取原始路径 --- ### **四、高级调试技巧** #### **1. 实时路径追踪** ```nginx location / { add_header X-Backend-Path "$scheme://$host$request_uri"; # 显示完整代理路径 proxy_pass http://java_backend:8080; } ``` **响应头示例**: ``` X-Backend-Path: http://java_backend:8080/user/list ``` --- #### **2. 流量镜像验证** ```nginx location / { proxy_pass http://java_backend:8080; mirror /mirror; # 将请求复制到调试端点 } location = /mirror { internal; proxy_pass http://debug_backend:8081$request_uri; } ``` - 主流量正常处理,副本流量发送至调试服务 --- ### **五、完整配置示例(含健康检查)** ```nginx upstream java_backend { server 192.168.1.10:8080 weight=5; server 192.168.1.11:8080; check interval=3000 rise=2 fall=3 timeout=1000 type=http; check_http_send "HEAD /health HTTP/1.0\r\n\r\n"; check_http_expect_alive http_2xx http_3xx; } server { listen 80; server_name api.example.com; # 根路径代理 location / { proxy_pass http://java_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # WebSocket支持 # 超时控制 proxy_connect_timeout 60s; proxy_read_timeout 600s; } # 健康检查端点 location /nginx_status { stub_status on; access_log off; allow 127.0.0.1; deny all; } } ``` --- ### **六、配置验证流程** 1. **基础连通性测试** ```bash curl -I http://nginx_host/health # 预期返回200状态 ``` 2. **路径完整性验证** ```bash curl http://nginx_host/api/v1/user | jq . # 查看返回数据结构是否符合预期 ``` 3. **Header传递验证** ```bash curl -H "X-Debug: true" http://nginx_host/ -v # 检查后端日志是否收到X-Debug头 ``` --- ### **--进阶问题--** 1. 如何实现基于URL参数的动态路由转发? 2. Nginx在代理根路径时如何处理`TRACE`等非常用HTTP方法? 3. 反向代理场景下如何配置HTTP/2到后端的协议升级? [^1]: Nginx路径保留与重写规则手册 [^2]: Location匹配优先级规则(前缀>正则>通用) [^3]: 上下文路径对服务发现的影响机制 [^4]: 特殊字符在代理过程中的编保留方案
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值