基于servlet的简单登录界面

news2024/11/23 13:47:11

前端登录发起请求

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 {
    @Override
    protected 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 {
    @Override
    protected 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 {
    @Override
    protected 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;//后端向前端响应的是CommonResult
        try{
            LoginDao loginDao=new LoginDao();
            Admin admin=loginDao.login(account,password);//将前端接收到的值  数据库里,正常响应admin
            if(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=111
         this.$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=111
         this.$http.post("login",jsonToString(this.form)).then((resp)=>{
             if(resp.data.code==200){
                 //提示
                  this.$message({message: resp.data.message,type: 'success'});
                 //在浏览器中存储用用户账号信息。resp.data是CommonResult
                  sessionStorage.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,账号生成token
    public 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 {
    @Override
    protected 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;//后端向前端响应的是CommonResult
        System.out.println(account+"::"+password);
        try{
            LoginDao loginDao=new LoginDao();
            Admin admin=loginDao.login(account,password);//将前端接收到的值  数据库里,正常响应admin
            if(admin!=null){
                //生成token
                String 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=111
         this.$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 {
    @Override
    protected 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 {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("token验证过滤器");
        //接收请求头中的adminToken
        //将ServletRequest向下转为HttpServletRequest
        HttpServletRequest 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.coloradmin.cn/o/725646.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Python Django实现KNN协同电影推荐系统

系统说明 基于Python Django实现KNN协同电影推荐系统&#xff0c;有用户端和管理后端&#xff0c;完整可在任何环境运行。 KNN&#xff08;K-Nearest Neighbor&#xff09;算法是机器学习算法中最基础、最简单的算法之一。它既能用于分类&#xff0c;也能用于回归。KNN通过测量…

使用前端JS上传文件到阿里云的OSS服务器,PHP生成STS临时访问凭证

官方教程地址&#xff1a;https://help.aliyun.com/document_detail/383950.html?spma2c4g.383952.0.0 这篇文章主要是指出官方教程没有说明的地方 后端代码 并非是完全完全不需要后端的参与。需要后端生成凭证&#xff0c;防止秘钥泄露 这里是官方的说明文档&#xff1a;使…

obj文件解析及用meshlab查看

举例 它以txt打开后如下所示 v -0.3 0 0.3 v 0.4 0 0 v -0.2 0.3 -0.1 v 0 0.4 0 # 4 verticesg head s 1 f 1/1/1 2/1/1 4/1/1 f 1/1/1 2/1/1 3/1/1 f 2/1/1 4/1/1 3/1/1 f 1/1/1 4/1/1 3/1/1一般而言obj文件以txt格式打开后包含如下片段 v -0.3 0 0.3 vt 0.625 0.458 0.00…

FPGA纯verilog实现UDP协议栈,GMII接口驱动88E1111,提供工程源码和技术支持

目录 1、前言2、我这里已有的UDP方案3、该UDP协议栈性能4、详细设计方案网络PHYGMII AXIS接口模块AXIS FIFOUDP协议栈 5、vivado工程1-->B50610 工程6、上板调试验证并演示准备工作查看ARPUDP数据回环测试 7、福利&#xff1a;工程代码的获取 1、前言 目前网上的fpga实现ud…

密码学学习笔记(七):Modular arithmetic - 模算数

简介 模算术是整数的一种算术结构&#xff0c;其中数字在达到特定值时“环绕”。模运算使我们能够简单地生成群、环和域&#xff0c;这是大多数现代公钥密码系统的基本构造部分。其中数字超过一定值后&#xff08;称为模&#xff09;后会“卷回”到较小的数值。 模算数常见的…

5.2ORB-SLAM3之回环矫正

1.简介 在上一章《回环检测之检测是否存共视区域》已经介绍了检测共视区域的部分&#xff0c;接下来就是对共视区域进行回环矫正或者地图融合。 回环矫正和之前的ORBSLAM系列一致&#xff0c;就是消除因为长时间运动产生的位姿累计误差和尺度漂移。在ORBSLAM3中新增了多地图系…

idea集成maven-mvnd

maven-mvnd是什么&#xff1f; 参考文档&#xff1a; Maven加强版 — mvnd的使用测试 - 知乎 1.下载mvnd安装包 Releases apache/maven-mvnd GitHub 2.修改配置文件&#xff1a;安装包中的conf目录下的mvnd.properties文件 配置maven settings的地址&#xff1a; 注意&am…

MySQL配置主从备份

文章目录 1.什么是主从备份2. 原理3.配置主服务器4.配置从服务器4.1进入数据库&#xff0c;准备建立连接4.2开启 slave 连接&#xff0c;主备机连接成功&#xff0c;数据开始同步4.3查看有关从属服务器线程的关键参数的信息 1.什么是主从备份 主从复制简单来说就是主库把增删改…

环境搭载vscode

Windows 10 下 VS Code 配置 C 开发环境&#xff08;MinGW&#xff09; 读书读傻了哟 配置 C/C 环境   主要是配置launch.json、tasks.json这两个文件&#xff08;当然&#xff0c;还有别的.json文件&#xff0c;可有可无&#xff09;。这两个文件位于.vscode文件夹下&#…

mysql--第一天基础操作

1.创建数据库 2.查询创建数据的语句 3.使用数据库&#xff0c;查询当前默认的数据库以及使用的编码方式校验规则 4.删除数据库 5.在一张表中定义多个字段&#xff0c;要使用今天提到的所有的数据类型&#xff08;数字&#xff0c;文本&#xff0c;日期&#xff09; 查看表结构

产品方案设计高效的4大注意事项

做产品方案时&#xff0c;我们容易遭遇&#xff1a;未澄清需求、未梳理业务方案、缺少思考过程以及缺少对比方案等误区&#xff0c;往往会造成产品方案并不能完全解决用户问题&#xff0c;项目后期容易遇到需求变更等风险。 因此如何如何高效设计产品方案&#xff1f;就显得尤为…

SpringCloud入门实战(十二)-Sleuth+Zipkin分布式请求链路跟踪详解

&#x1f4dd; 学技术、更要掌握学习的方法&#xff0c;一起学习&#xff0c;让进步发生 &#x1f469;&#x1f3fb; 作者&#xff1a;一只IT攻城狮 &#xff0c;关注我&#xff0c;不迷路 。 &#x1f490;学习建议&#xff1a;1、养成习惯&#xff0c;学习java的任何一个技术…

Django的数据库配置、生成(创建)过程、写入数据、查看数据的学习过程记录

目录 01-配置数据库信息02-安装Python的MySQL数据库驱动程序 mysqlclient03-安装Mysql&#xff0c;并启动Mysql04-定义Django的数据库模型(定义数据表-编写models.py文件)05-按照数据的配置生成数据库(执行迁移命令)05-01-生成迁移执行文件05-02-执行数据库模型迁移 06-查看数据…

Vue.js Js引入相关

Vue.js vue.js 新增了一些语法,有一些旧的模组并没有使用"先进"的export和import语法 即 es语法进行模块化。 <script></script>但 editor.md 真的很好用. 但很抱歉,它在vue中无法使用 es6 进行导入。 所以需要使用传统的方式进行导入。 很多人会把js…

【ARM Coresight 系列文章 2.1 - ARM Coresight 组件介绍】

文章目录 1.1 Coresight 组件介绍1.1.1 Trace sources1.1.2 Trace Sinks1.1.2 Trace links 1.1 Coresight 组件介绍 图 1-1 1.1.1 Trace sources 什么是 Trace source? 在ARM Coresight技术中&#xff0c;Trace Source是指处理器中的一个组件&#xff0c;用于产生和发送跟踪数…

全球十大看黄金走势免费app软件最新名单推荐(综合版)

选择黄金走势免费app软件时&#xff0c;有几个关键因素需要考虑。首先&#xff0c;要选择可靠的软件平台&#xff0c;确保其在金融市场上拥有良好的声誉和高度的信任度。此外&#xff0c;软件应提供及时准确的市场数据&#xff0c;包括实时行情、交易量和技术指标等&#xff0c…

高速PCB布局布线规范

目录 一、容抗/感抗 1.容抗 2.感抗 二、寄生电容/分布电容/杂散电容 1.寄生电容 2.分布电容 3.杂散电容 4.寄生电容/分布电容/杂散电容对信号的影响 5.怎么减小分布电容&#xff1f; 三.寄生电感 1.什么是寄生电感&#xff1f; 2.怎么减小寄生电感&#xff1f; 四.…

ROS:节点名称重名

目录 一、前言二、rosrun设置命名空间与重映射2.1设置命名空间2.2rosrun名称重映射2.3rosrun命名空间与名称重映射叠加 三、launch文件设置命名空间与重映射四、编码设置命名空间与重映射4.1C 实现:重映射4.2C 实现:命名空间4.3Python 实现:重映射 一、前言 ROS 中创建的节点是…

Python3在Windows上设置环境变量方法

Python3在Windows上设置环境变量方法&#xff0c;在环境变量中添加Python目录&#xff1a; 在命令提示框中(cmd) : 输入 path%path%;C:\Python 按下"Enter"。 注意: C:\Python 是Python的安装目录。 也可以通过以下方式设置&#xff1a; 右键点击"计算机&q…

《深入理解计算机系统》(9)内存管理

1、物理和虚拟寻址 物理寻址 主存被组织成一个由 M 个连续的字节大小的单元组成的数组。每字节都有一个唯一的物理地址。CPU 访问内存最自然的方式就是使用物理地址&#xff0c;称为物理寻址。下图是一个物理寻址的示例&#xff0c;该示例的上下文是一个加载指令&#xff0c;它…