基于servlet的简单登录界面

前端登录发起请求

1.安装axios

axios 是一个 HTTP 的网络请求库

  1. 安装 npm install axios (脚手架中)

  1. 在 main.js 中配置 axios

//导入网络请求库
import axios from 'axios';
//设置访问后台服务器地址:ip,端口和项目名字,请求时会自动和Login.vue中的this.$http.post("login")拼起来
axios.defaults.baseURL="http://127.0.0.1:8080/webBack/";
//将 axios 挂载到 vue 全局对象中,$http表示axios对象
Vue.prototype.$http=axios;

2.组装请求数据

提交更多的数据写在data的form(名字自定义的)中,form是个整体,提交给后端;表单中也要进行修改

将json对象序列化为键=值&键=值

<template><div class="login_container"><!-- 登录盒子--><div class="login_box"><!-- 头像盒子--><div class="img_box"><img src="./assets/logo.png" /></div><div style="margin-top: 100px; padding: 20px;"><el-form ref="form" :model="form" label-width="80px"><!--:model="form"--><el-form-item label="账号"><el-input v-model="form.account"></el-input></el-form-item><el-form-item label="密码"><el-input v-model="form.password" type="password"></el-input></el-form-item><el-form-item><el-button type="success" @click="login">登录</el-button><el-button class="el-icon-delete">取消</el-button></el-form-item></el-form></div></div></div>
</template><script>// 导出组件
export default{data(){return{form:{account:"",password:""}}},methods:{login(){//前端post向后端(这里的login为:后端LoginServlet的地址)发送post请求,然后回调(then)给前端 响应前端。接受到后端返回的数据,然后在执行以下内容this.$http.post("login",jsonToString(this.form)).then((resp)=>{//$http表示axios对象this.$router.push("/main");//前端进行路由跳转,this表示vue对象})}},mounted(){}
}//将json对象序列化为键=值&键=值function jsonToString(jsonobj){console.log(jsonobj)var str = "";for(var s in jsonobj){str+=s+"="+jsonobj[s]+"&";      }return str.substring(0,str.length-1);}
</script><style>.login_container{height: 100vh;margin: 0px;padding: 0px;background-image: url(assets/bg.jpg);}.login_box{width: 450px;height: 350px;background-color: #fff;border-radius: 10px;position: absolute;left: 50%;top: 50%;transform: translate(-50%,-50%);opacity: 0.95;}.img_box{width: 130px;height: 130px;position: absolute;left: 50%;transform: translate(-50%,-50%);background-color: #fff;border-radius: 50%;padding: 5px;border: 1px solid #eee;}.img_box img{width: 100%;height: 100%;border-radius: 50%;background-color: #eee;}
</style>

后端接收请求

package Servlet;public class LoginServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        req.setCharacterEncoding("utf-8");//设置请求中数据解码的编码格式String account=req.getParameter("account");String password=req.getParameter("password");System.out.println(account+"::"+password);}
}

这时,在前端点击登录按钮之后,后端就会接收到 用户名::密码

连接数据库

1.创建web_db数据库和admin表

create database web_db charset utf8
create table admin(id int primary key auto_increment,account varchar(20) unique,password varchar(20)
)
package model;public class Admin {private int id;private String account;private String password;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getAccount() {return account;}public void setAccount(String account) {this.account = account;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}
}

jdbc连接数据库

要返回admin,最好别返回true false,因为通过账号密码登录,查询显示用户其他更多的数据

package dao;public class LoginDao {public Admin login(String account, String password) throws ClassNotFoundException, SQLException {Connection connection=null;PreparedStatement ps=null;Admin admin=null;//要返回admin,最好别返回true false,因为通过账号密码登录,然后显示别的更多的数据try{Class.forName("com.mysql.cj.jdbc.Driver");//动态加载Driver类String url="jdbc:mysql://127.0.0.1:3306/web_db?serverTimezone=Asia/Shanghai";String uname="root";String psd="123456";//出现的异常都抛出去 不处理,抛给LoginServlet,因为LoginServlet负责和用户进行交互connection= DriverManager.getConnection(url,uname,psd);ps=connection.prepareStatement("select id,account from admin where account=? and password=?");//登录是查询语句,有条件的进行查询ps.setObject(1,account);ps.setObject(2,password);ResultSet rs=ps.executeQuery();while(rs.next()){//查询有数据admin = new Admin();admin.setId(rs.getInt("id"));admin.setAccount(rs.getString("account"));}}finally {if(connection!=null){//connection==null时,本来就是关闭的状态connection.close();}if(ps!=null){ps.close();}}return admin;}
}
public class LoginServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        req.setCharacterEncoding("utf-8");//设置请求中数据解码的编码格式String account=req.getParameter("account");String password=req.getParameter("password");//System.out.println(account+"::"+password);try{LoginDao loginDao=new LoginDao();Admin admin=loginDao.login(account,password);//将前端接收到的值在数据库里查找,存到Admin}catch (Exception e){}}
}

后端响应

1.接收到dao返回的查询数据

2.进行数据封装,创建了一个CommonResult类:code,data,message

3.进行逻辑判断

4.异常处理

package model;
//用来封装后端的数据
public class CommonResult {private int code;//自定义的状态码private Object data;//数据 对象 集合private String message;//消息public CommonResult(int code, Object data, String message) {this.code = code;this.data = data;this.message = message;}public CommonResult(int code, String message) {this.code = code;this.message = message;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public Object getData() {return data;}public void setData(Object data) {this.data = data;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}
}
package Servlet;public class LoginServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        req.setCharacterEncoding("utf-8");//设置请求中数据解码的编码格式//接收登录表单数据String account=req.getParameter("account");String password=req.getParameter("password");resp.setContentType("text/html;charset=utf-8");//设置响应编码PrintWriter printWriter= resp.getWriter();//拿到响应的输出流对象CommonResult commonResult=null;//后端向前端响应的是CommonResulttry{LoginDao loginDao=new LoginDao();Admin admin=loginDao.login(account,password);//将前端接收到的值  数据库里,正常响应adminif(admin!=null){commonResult=new CommonResult(200,admin,"登录成功");}else{commonResult=new CommonResult(201,"账号或密码错误!");}}catch (Exception e){e.printStackTrace();//打印异常信息commonResult=new CommonResult(500,"系统忙");}ObjectMapper objectMapper=new ObjectMapper();String json=objectMapper.writeValueAsString(commonResult);printWriter.print(json);}
}

可以在网络中的响应查看CommonResult的信息

前端处理

1.前端接收数据处理

2.前端路由跳转

methods:{login(){//预留与后端进行一次交互  account=admin&password=111this.$http.post("login",jsonToString(this.form)).then((resp)=>{if(resp.data.code==200){//resp.data是后端中的CommonResult//提示this.$message({message: resp.data.message,type: 'success'});//路由跳转this.$router.push("/main");}if(resp.data.code==201){this.$message({message: resp.data.message,type: 'warning'});}if(resp.data.code==500){this.$message.error(resp.data.message);}})}
},

3.前端存储用户信息

sessionStorage,浏览器关闭之后会话就结束了,结束之后信息消失,下次登录之后重新存储信息

methods:{login(){//预留与后端进行一次交互  account=admin&password=111this.$http.post("login",jsonToString(this.form)).then((resp)=>{if(resp.data.code==200){//提示this.$message({message: resp.data.message,type: 'success'});//在浏览器中存储用用户账号信息。resp.data是CommonResultsessionStorage.setItem("account",resp.data.data.account);//路由跳转this.$router.push("/main");}if(resp.data.code==201){this.$message({message: resp.data.message,type: 'warning'});}if(resp.data.code==500){this.$message.error(resp.data.message);}})}
},

4.在main组件中显示用户账号

data定义account

在声明周期函数里,拿到浏览器存储的account

然后挂载到页面上

<template><el-container><!-- 顶部--><el-header><div style="width: 500px; display: inline-block; font-size: 26px;font-weight: 400; color: #fff;">管理系统后台</div><el-dropdown style="float: right;"><i class="el-icon-setting" style="margin-right: 15px"><span>{{account}}</span></i><el-dropdown-menu slot="dropdown"><el-dropdown-item>修改密码</el-dropdown-item><el-dropdown-item>安全退出</el-dropdown-item></el-dropdown-menu></el-dropdown></el-header><el-container><!-- 左边--><el-aside width="200px"><el-menu :default-openeds="['1', '3']"><el-submenu index="1"><template slot="title"><i class="el-icon-message"></i>操作菜单</template><el-menu-item index="1-1">学生管理</el-menu-item><el-menu-item index="1-2">老师管理</el-menu-item></el-submenu></el-menu></el-aside><!-- 工作区--><el-main>Main</el-main></el-container></el-container>
</template><script>export default{data(){return{account:""}},methods:{},mounted() {this.account=sessionStorage.getItem("account");}}
</script>

路由导航守卫

没有登录,不能直接访问非登录的页面

可以在Main.vue中进行判断,但是不建议。因为除了登录页面不需要进行判断,其他的页面都需要判断(要在每个组件中都判断),很麻烦

<script>export default{data(){return{account:""}},methods:{},mounted() {this.account=sessionStorage.getItem("account");if(this.account==null){this.$router.push("/login");}}}
</script>

建议:在main.js 中判断

为路由对象,添加 beforeBach导航守卫

to-将要访问的页面地址,from-从哪个页面访问的,next-放行函数

//路由导航守卫,每次发生路由跳转时,就会自动的执行此段逻辑
rout.beforeEach((to, from, next) => {if (to.path == '/login') {//如果用户访问的登录页, 直接放行return next();//放行, 去to.path} else {var account = window.sessionStorage.getItem("account");//从浏览器中取出用户信息if (account == null) {//表示没有登录return next("/login");} else {//已经登陆next();}}
})

web会话跟踪

从一个客户打开浏览器并连接到服务器开始,到客户关闭浏览器离开这个服务器结束,被称为一个会话。

为什么要会话跟踪?

http是无状态的,登录完成后,客户端就与服务器断开了连接之后再向后端发送请求,后端就不知道是哪个客户端发送的,会话进行跟踪就是为了解决这样的问题。

会话跟踪是Web程序中常用的技术,用来跟踪用户的整个会话过程。给客户端们颁发一个通行证,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份

token

token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后, 服务器生成一个token便将此token返回给客户端,以后客户端只需带上这个token前来请求数据即可。token保存在客户端,并且进行了加密,保证了数据的安全性。

目的:token的目的是为了减轻服务器的压力,使服务器更加健壮。

JWT

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一 种基于 JSON 的开放标准(RFC 7519)。定义了一种简洁的,自包含的方法用于通信双方之间以JSON 对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT 可以使用 HMAC 算法或者是 RSA 的公私秘钥对进行签名。

基于 token 的鉴权机制

基于 token 的鉴权机制类似于 http 协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于 token 认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。

JWT 的主要应用场景

身份认证在这种场景下,一旦用户完成了登陆,在接下来的每个请求中包含JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由于它的开销非常小,可以轻松的在不同域名的系统中传递,所有目前在单点登录中比较广泛的使用了该技术。信息交换在通信的双方之间使用 JWT 对数据进行编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的。

优点

1.简洁(Compact): 可以通过 URL,POST 参数或者在 HTTP header 发送,因为数据量小,传输速度也很快

2.自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库

3.因为 Token 是以 JSON 加密的形式保存在客户端的,所以 JWT 是跨语言的,原则上任何 web 形式都支持。

4.不需要在服务端保存会话信息,特别适用于分布式微服务。

JWT 的构成

JWT 是由三段信息构成的,将这三段信息文本用.链接一起就构成了 Jwt 字符串。

就像这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 用户的信息),第三部分是签证(signature).

🌈第一部分 header

jwt 的头部承载两部分信息:

声明类型,这里是 jwt

声明加密的算法 通常直接使用 HMAC HS256

完整的头部就像下面这样的 JSON:

{
'typ': 'JWT',
'alg': 'HS256'
}

然后将头部进行 base64 转码,构成了第一部分.eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

🌈第二部分 playload

载荷就是存放有效信息的地方。这些有效 信息包含三个部分

标准中注册的声明

公共的声明。公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息(例如密码),因为该部分在客户端可解密. id,用户名,头像名

私有的声明

定义一个 payload

{
"sub": "1234567890","name": "John Doe",
"admin": true
}

然后将其进行 base64 转码,得到 Jwt 的第二部分。 eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

🌈第三部分 signature

jwt 的第三部分是一个签证信息,这个签证信息由三部分组成:

header (base64 后的)

payload (base64 后的)

secret

这个部分需要 base64 转码后的 header 和 base64 转码后的 payload 使用.连接组成的字符串,然后通过 header 中声明的加密方式进行加盐 secret 组合加密,然后就构成了 jwt 的第三部分。TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

base64是什么:64个基础编码字符,以六个比特位算出来对应的编号,去编码表找对应的字符

使用会话跟踪:

1.登录 向后端发送账号和密码
2.后端与数据库连接验证账号密码
3.如果账号密码正确,在后端生成一个token(令牌 唯一的),把token响应给前端
  1. lib中添加

java-jwt-3.8.2.jar

commons-codec-1.15.jar

  1. 在util中写JWTUtil

package util;import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import model.Admin;import java.util.Date;
import java.util.HashMap;
import java.util.Map;//JWT工具类
public class JWTUtil {//根据用户id,账号生成tokenpublic static String getToken(Admin admin) {String token = "";try {//过期时间 为1970.1.1 0:0:0 至 过期时间  当前的毫秒值 + 有效时间Date expireDate = new Date(new Date().getTime() + 3600*1000);//秘钥及加密算法Algorithm algorithm = Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE");//设置头部信息Map<String,Object> header = new HashMap<>();header.put("typ","JWT");header.put("alg","HS256");//携带id,账号信息,生成签名token = JWT.create().withHeader(header).withClaim("id",admin.getId()).withClaim("account",admin.getAccount()).withExpiresAt(expireDate).sign(algorithm);}catch (Exception e){e.printStackTrace();return  null;}return token;}//验证token是否有效public static boolean verify(String token){try {//验签Algorithm algorithm = Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE");JWTVerifier verifier = JWT.require(algorithm).build();DecodedJWT jwt = verifier.verify(token);return true;} catch (Exception e) {//当传过来的token如果有问题,抛出异常return false;}}
//获得token 中playload部分数据,按需使用public static DecodedJWT getTokenInfo(String token){return JWT.require(Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE")).build().verify(token);}
}

将生成的token存到admin,然后响应给前端

package Servlet;public class LoginServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        req.setCharacterEncoding("utf-8");//设置请求中数据解码的编码格式//接收登录表单数据String account=req.getParameter("account");String password=req.getParameter("password");resp.setContentType("text/html;charset=utf-8");//设置响应编码PrintWriter printWriter= resp.getWriter();//拿到响应的输出流对象CommonResult commonResult=null;//后端向前端响应的是CommonResultSystem.out.println(account+"::"+password);try{LoginDao loginDao=new LoginDao();Admin admin=loginDao.login(account,password);//将前端接收到的值  数据库里,正常响应adminif(admin!=null){//生成tokenString token=JWTUtil.getToken(admin);admin.setToken(token);commonResult=new CommonResult(200,admin,"登录成功");}else{commonResult=new CommonResult(201,"账号或密码错误!");}}catch (Exception e){e.printStackTrace();//打印异常信息commonResult=new CommonResult(500,"系统忙");}ObjectMapper objectMapper=new ObjectMapper();String json=objectMapper.writeValueAsString(commonResult);printWriter.print(json);}
}
package model;public class Admin {private int id;private String account;private String password;private String token;public String getToken() {return token;}public void setToken(String token) {this.token = token;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getAccount() {return account;}public void setAccount(String account) {this.account = account;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}
}
4.在前端存储token
methods:{login(){//预留与后端进行一次交互  account=admin&password=111this.$http.post("login",jsonToString(this.form)).then((resp)=>{if(resp.data.code==200){//提示this.$message({message: resp.data.message,type: 'success'});//在浏览器中存储用户账号信息sessionStorage.setItem("account",resp.data.data.account);sessionStorage.setItem("adminToken",resp.data.data.token)//路由跳转this.$router.push("/main");}if(resp.data.code==201){this.$message({message: resp.data.message,type: 'warning'});}if(resp.data.code==500){this.$message.error(resp.data.message);}})}
},
5.之后的每次请求,都将token携带着向后端发送

Main.vue,向后端再次发送一个get请求

<script>export default{data(){return{account:""}},methods:{test(){this.$http.get("back/test").then((resp)=>{if(resp.data.code==200){}});}},mounted() {this.account = sessionStorage.getItem("account");}}
</script>

请求拦截,在main.js中,这样就不需要每次请求时添加Token

//axios ,请求拦截,每发送一次http请求,都会执行此拦截器
axios.interceptors.request.use(config =>{
//为请求头对象,添加 Token 验证的 token 字段config.headers.adminToken = window.sessionStorage.getItem('adminToken');return config;
})

请求成功之后,后端控制台打印:请求成功

package Servlet;public class TestServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("请求成功");}
}
6.后端的java对请求中的token进行解析验证

后将此token每次与后端交互时,都放在请求头

后端接收到token后,会对token进行验证是否符合规则,符合继续执行,不符合直接响应验在后端进行生产token,在后端验证token能够保证安全性

package filter;
public class AdminTokenFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("token验证过滤器");//接收请求头中的adminToken//将ServletRequest向下转为HttpServletRequestHttpServletRequest request=(HttpServletRequest)servletRequest;String adminToken = request.getHeader("adminToken");boolean verify = JWTUtil.verify(adminToken);//验证token是否有效if(verify){filterChain.doFilter(servletRequest,servletResponse);//继续向后执行}else{//token验证失败servletResponse.setContentType("text/html;charset=utf-8");//设置响应编码PrintWriter printWriter= servletResponse.getWriter();//拿到响应的输出流对象CommonResult commonResult=new CommonResult(401,"token验证失败,请重新登陆");ObjectMapper objectMapper=new ObjectMapper();String json=objectMapper.writeValueAsString(commonResult);printWriter.print(json);}}
}
<!--除了登录以外的请求地址,都加一个统一的前缀。就和登录的地址区分开-->
<filter><filter-name>adminTokenFilter</filter-name><filter-class>filter.AdminTokenFilter</filter-class>
</filter>
<filter-mapping><filter-name>adminTokenFilter</filter-name><url-pattern>/back/*</url-pattern>
</filter-mapping>
7.在前端添加响应拦截器

main.js中,添加响应拦截器。可以将后端500,202 (token验证失败) 这些统一的状态码,进行拦截,减少了代码重复

// 添加响应拦截器
axios.interceptors.response.use((resp) => { //正常响应拦截if (resp.data.code == 500) {ElementUI.Message({message: resp.data.message,type: "error"})}if (resp.data.code == 401) {sessionStorage.clear();//token独证失败,认为登录失效,也可以清空前端存储的信息,重新让其登录router.replace("/login");}return resp;
});

如果token不对,会被响应拦截器拦截,返回登录页面

安全退出

<template><el-container><!-- 顶部--><el-header><div style="width: 500px; display: inline-block; font-size: 26px;font-weight: 400; color: #fff;">管理系统后台</div><el-dropdown style="float: right;"><i class="el-icon-setting" style="margin-right: 15px"><span>{{account}}</span></i><el-dropdown-menu slot="dropdown"><el-dropdown-item>修改密码</el-dropdown-item><el-dropdown-item><span @click="logOut()">安全退出</span></el-dropdown-item><el-dropdown-item><span @click="test()">测试</span></el-dropdown-item></el-dropdown-menu></el-dropdown></el-header><el-container><!-- 左边--><el-aside width="200px"><el-menu :default-openeds="['1', '3']"><el-submenu index="1"><template slot="title"><i class="el-icon-message"></i>操作菜单</template><el-menu-item index="1-1">学生管理</el-menu-item><el-menu-item index="1-2">老师管理</el-menu-item></el-submenu></el-menu></el-aside><!-- 工作区--><el-main>Main</el-main></el-container></el-container>
</template><script>export default{data(){return{account:""}},methods:{logOut(){this.$confirm('您确定要退出吗?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {sessionStorage.clear();//清除浏览器中存储的用户信息this.$router.push("/login");})},test(){this.$http.get("back/test").then((resp)=>{if(resp.data.code==200){}});}},mounted() {this.account = sessionStorage.getItem("account");}}
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/19015.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

hbuilderX自定义主题仿vscode暗黑主题

目录 hbuilderX自定义主题仿vscode暗黑主题 效果图 设置方式&#xff08;把主题代码复制进来即可&#xff09; 是基于雅蓝主题自定义的 设置好后需要切换到雅蓝主题 hbuilderx保存时自动整理代码 1.首先在顶部栏&#xff0c;找到工具&#xff0c;打开设置 2.点击旁边的编…

盖子的c++小课堂——第十八讲:栈

目录 前言 栈的定义 栈&#xff0c;是什么&#xff1f; 例1-弹夹 问题 例2-停车场 问题 栈的概念 空栈 进栈、出栈 特点 例题 车厢调度 如何操作 数组模拟栈 入栈 出栈 栈的基本操作 判断空栈 求栈的元素数量 读栈顶元素 总结 前言 OK呀&#xff0c;说到做…

【unity细节】为什么有时候修改了脚本中的数据但是面板中的数据还是不改变

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 秩沅 原创 收录于专栏&#xff1a;unity细节和bug ⭐为什么有时候修改了脚本中的数据但是面板中的数据还是不改变⭐ 文章目录 ⭐为什…

微服务之Eureka服务注册中⼼

关于务注册中⼼服 服务注册中⼼本质上是为了解耦服务提供者和服务消费者,尽可能量使两者联系可控在一定的范围外 1.在父项目下下引入 Spring Cloud 依赖 <dependencyManagement> <dependencies> <!-- SCN --> <dependency> <groupId> org.sp…

分销商城系统有哪些模式,有什么功能和特点?

随着微信的广泛使用&#xff0c;微信在营销方面的发展呈现出多元化趋势&#xff0c;衍生出很多基于社交环境的营销模式。分销商城系统就是其中之一&#xff0c;很多企业都通过分销商城系统发展自己的分销商&#xff0c;充分挖掘微信社交体系的私域资源。下面我们一起来了解一下…

【单周期CPU】LoongArch | LA32R | 二选一控制器MUX | 数据通路

前言&#xff1a;本章内容主要是演示在vivado下利用Verilog语言进行单周期简易CPU的设计。一步一步自己实现模型机的设计。本章先介绍单周期简易CPU中数据通路的设计。 &#x1f4bb;环境&#xff1a;一台内存4GB以上&#xff0c;装有64位Windows操作系统和Vivado 2017.4以上版…

嵌入式Linux领域中的C++:应用层主力与挑战

嵌入式Linux领域中&#xff0c;C是应用层的主要编程语言之一。尽管QT/C在某些领域被Android/Java逐渐替代&#xff0c;但在医疗、工控、车载导航等领域仍占主导地位。对于嵌入式人员学习C的观点存在一定片面性。C的难点在于移动语义、模板偏特化、lambda表达式、模板元编程等知…

QInputDialog 不显示ok或cancel按钮bug

今天遇到一个奇怪问题&#xff0c;就是调用 QInputDialog::getText去获取输入文本&#xff0c;但是无法显示系统ok和cancel按钮&#xff0c;我记得之前是可以的&#xff0c;于是我回退上一个版本是正常&#xff0c;于是对比两个版本代码&#xff0c;发现&#xff0c;自己重写 Q…

基于Python制作一个简单的文章搜索工具

这篇文章主要为大家详细介绍了如何基于Python制作一个简单的文章搜索工具&#xff0c;都是一些基础的应用&#xff0c;文中的示例代码讲解详细&#xff0c;感兴趣的可以了解一下 前言 今天&#xff0c;我无聊的时候做了一个搜索文章的软件&#xff0c;有没有更加的方便快捷不知…

go-zero微服务实战——基本环境搭建

简介 项目架构来源于go-zero实战&#xff1a;让微服务Go起来。此对该项目有所删减&#xff0c;相对简单适合初学者。 省去了项目中每个服务占用独立docker的过程&#xff0c;省略了docker-compose的构建过程。每个服务是一个独立的程序不依赖与容器。 环境搭建 安装goctl …

033、TiDB特性_AUTO_INCREMENT

自增列 实现原理使用限制相关参数示例 实现原理 每一个自增列使用一个全局可见的键值对用于记录当前已分配的最大ID为了降低分布式系统分配自增ID的网络开销&#xff0c;每个TiDB节点会缓存一个不重复的ID段当前预分配的ID段使用完毕&#xff0c;或重启&#xff0c;都会重新再…

使用楔形步进体模进行X射线骨密度测定

来源&#xff1a;投稿 作者&#xff1a;洪棋 编辑&#xff1a;学姐 骨密度(BMD)被广泛应用于骨折风险的预测和骨质疏松症的常规识别。双能x线骨密度仪(DXA)在临床上广泛用于测量脊柱、髋关节和前臂的骨密度(aBMD)。放射学骨密度测定法(Radiographic absorpometry, RA)是最早的骨…