当前位置: 首页 > news >正文

JavaEE项目总结(1)

一、在vue项目中安装axios
 

由于需要使用axios框架进行异步请求,所以需要在vue项目中安装axios框架。在官方下载速度较慢,所以选择更换镜像源(我使用的是华为云镜像)

在项目终端中输入npm config set registry http://mirrors.cloud.tencent.com/npm/

更换后通过在终端输入npm config get registry检查当前镜像源

二、前端向后端发送请求 

用户在浏览器进行操作,而这些操作最开始到达的是前端。这时候需要前端向后端发出响应。

我们以一个vue项目的登录功能为例,当用户输入了账户密码并点击登录是,前端需要拿到账户密码的数据并向后端发送

<!-- 一个.vue文件是一个组件,可以理解为一个页面,但是和页面不同 内容都写在一个template标签中,template标签必须有一个根标签
-->
<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-right: 50px;"><el-form ref="form" label-width="80px"><!-- 第一栏 --><el-form-item label="账号"><el-input v-model="account"></el-input></el-form-item><!-- 第二栏 --><el-form-item label="密码" show-password><el-input v-model="password"></el-input></el-form-item><!-- 按钮 --><el-form-item><el-button type="primary"  @click="save()">登录</el-button><el-button>取消</el-button></el-form-item></el-form>  </div></div></div>
</template><script>
/* 导出组件,并为组件定义数据,函数,生命周期函数 */export default{data(){return{account:'admin',password:'111'}},methods:{save(){if(this.account.length==0){this.$message({message: '账号不得为空',type: 'warning'});return;}if(this.password.length==0){this.$message({message: '密码不得为空',type: 'warning'});return;		}// 向后端交互"/login","account="+this.account+"&password="+this.passwordthis.$http.post("login","account="+this.account+"&password="+this.password).then((resp)=>{/* 后端响应的结果 */})}}}
</script><style>.login_container{height: 100vh;margin: 0px;padding: 0px;background-image: url(assets/bg.png);}.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>

三、后端请求的处理

        后端处理主要分成三个步骤,简单来说就是"接化发"。

        接是接收来自前端的请求,化是在后端进行处理,发是对前端进行响应。这三个步骤也是后端的基本作用

登录servlet

package com.wbc.dorm.web;import com.fasterxml.jackson.databind.ObjectMapper;
import com.wbc.dorm.dao.LoginDao;
import com.wbc.dorm.model.Admin;
import com.wbc.dorm.model.Result;
import com.wbc.dorm.util.JWTUtil;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;/*登录处理servlet*/
@WebServlet(urlPatterns = "/login",name = "login", loadOnStartup = 1)
public class LoginServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {/*登录请求的账号密码*/String account = req.getParameter("account");String password = req.getParameter("password");//System.out.println(account);//System.out.println(password);Result result = null;PrintWriter printWriter = resp.getWriter();/*调用dao层查询账号密码是否正确*/try {LoginDao loginDao = new LoginDao();Admin admin = loginDao.login(account, password);//System.out.println(admin.toString());if (admin!=null){//将admin放入标准化响应模型result = new Result(200, "登陆成功", admin);}else {result = new Result(201, "账号或密码错误", null);}} catch (Exception e) {e.printStackTrace();result = new Result(500, "系统忙", null);}ObjectMapper objectMapper = new ObjectMapper();String jsonString = objectMapper.writeValueAsString(result);printWriter.print(jsonString);}
}

管理员模型

package com.wbc.dorm.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;}@Overridepublic String toString() {return "Admin{" +"id=" + id +", account='" + account + '\'' +", password='" + password + '\'' +'}';}
}

与数据库交互的dao

package com.wbc.dorm.dao;import com.wbc.dorm.model.Admin;
import com.wbc.dorm.model.Admin;import java.sql.*;public class LoginDao {public Admin login(String username, String password) throws ClassNotFoundException, SQLException {Class.forName("com.mysql.cj.jdbc.Driver");String url ="jdbc:mysql://127.0.0.1:3306/dormdb?serverTimezone=Asia/Shanghai";//定义连接sql所需的urlString users ="root";//用户名String passwords ="Wbc11280";//密码//建立连接Connection connection = DriverManager.getConnection(url,users,passwords);//建立连接//预编译PreparedStatement preparedStatement =connection.prepareStatement("select id,account from admin where account = ? and password = ?");try{//传入数据preparedStatement.setObject(1, username);preparedStatement.setObject(2, password);//查询操作ResultSet resultSet = preparedStatement.executeQuery();//将查询结构封装到ResultSet类型的对象中 需要将数据封装到指定类型的对象中Admin admin = null;while (resultSet.next()) {admin = new Admin();admin.setId(resultSet.getInt("id"));admin.setAccount(resultSet.getString("account"));
//                System.out.println(admin.toString());}return admin;//return null;}finally {if(preparedStatement != null){preparedStatement.close();}if(connection != null){connection.close();}}}
}

四、前端接收响应并做处理

当后端程序向前端响应后,前端需要接收响应并作出响应的处理

// 向后端交互"/login","account="+this.account+"&password="+this.passwordthis.$http.post("login","account="+this.account+"&password="+this.password).then((resp)=>{/* 后端响应的结果 */if(resp.data.code==200){sessionStorage.setItem("account",resp.data.data.account);//存储到绘画空间,浏览器内部存储this.$router.push("/main");}else if(resp.data.code==201){this.$message({message:resp.data.message,type:'warning'})}else if(resp.data.code==500){this.$message({message:resp.data.message,type:'warning'})}})

根据后端传来的标准化响应模型对象中的状态码,来做出对用户的响应,如登陆成功跳转到主页面,登陆失败根据状态码给出相应提示

说明:

        sessionStorage.setItem(key, value)可以将浏览器中的数据存储到浏览器中的会话空间,只要窗口打开,数据就可以一直使用,直到界面关闭或者通过sessionStorage.clear()方法手动清楚。会话空间的数据可以通过sessionStorage.getItem(key)方法取出,从而在整个项目中共享数据。

五、路由导航守卫

当我们有了登录验证功能之后,肯定不希望用户通过路由导航(直接输入网址)的方式越过登录直接进入到主页面。如果在每次跳转之后都进行判断则相当的麻烦

<template><div><el-container><el-header style="text-align: right; font-size: 12px"><div class="header-title">后台管理系统</div><el-dropdown><i class="el-icon-setting" style="margin-right: 15px"></i><el-dropdown-menu slot="dropdown" ><el-dropdown-item>主页</el-dropdown-item><el-dropdown-item>修改密码</el-dropdown-item><el-dropdown-item><span @click="logout()">安全退出</span></el-dropdown-item></el-dropdown-menu></el-dropdown><span>{{account}}</span></el-header><el-container><el-aside width="200px" style="background-color: rgb(238, 241, 246)"><el-menu :default-openeds="['1', '3']" router><el-submenu index="1"><template slot="title"><i class="el-icon-message"></i>操作菜单</template><el-menu-item-group><el-menu-item index="/majorlist">专业管理</el-menu-item><el-menu-item index="/studentlist">学生管理</el-menu-item><el-menu-item index="1-3">学院管理</el-menu-item></el-menu-item-group></el-submenu></el-menu></el-aside><el-main><router-view></router-view></el-main></el-container></el-container></div>
</template><script>export default{data(){return{account:"",}},methods:{logout(){this.$confirm('您确定要退出么?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {this.$message({type: 'success',message: '退出成功成功!'});sessionStorage.clear();this.$router.push("/login")})}},mounted(){this.account=window.sessionStorage.getItem("account");//将account存入路由器内部console.log(this.account);if(account==null){this.$router.push("/login")}}}
</script><style>.el-header {background-color: #00a7fa;color: #333;line-height: 60px;}.header-title{width: 300px;float: left;text-align: left;font-size: 20px;color: white;}.el-main{background-color: aliceblue;height: 100vh;}
</style>

需要在每一次跳转中都进行一次判断。

而我们所使用的axios框架为我们提出了更加方便的解决方法------路由导航守卫。我们可以通过在路由中配置路由导航守卫,在每一次跳转时自动执行代码,进行判断是否已经登录

在/router/index.js文件中定义路由组件代码下方加入如下代码

//配置路由导航守卫
//每当进行一次组件路由时,就会触发导航守卫
rout.beforeEach((to,from,next)=>{if(to.path=='/login'){//如果用户访问的登录页,直接放行return next();//继续访问}	else{var account = window.sessionStorage.getItem("account");//获取路由器中存储的管理员信息if(account==null){//如果为空说明没有登录return next("/login");//跳转到登陆页面}else{//已经登陆next();//继续访问}}
})

如此我们就快要省去每次跳转判断的代码

<template><div><el-container><el-header style="text-align: right; font-size: 12px"><div class="header-title">后台管理系统</div><el-dropdown><i class="el-icon-setting" style="margin-right: 15px"></i><el-dropdown-menu slot="dropdown" ><el-dropdown-item>主页</el-dropdown-item><el-dropdown-item>修改密码</el-dropdown-item><el-dropdown-item><span @click="logout()">安全退出</span></el-dropdown-item></el-dropdown-menu></el-dropdown><span>{{account}}</span></el-header><el-container><el-aside width="200px" style="background-color: rgb(238, 241, 246)"><el-menu :default-openeds="['1', '3']" router><el-submenu index="1"><template slot="title"><i class="el-icon-message"></i>操作菜单</template><el-menu-item-group><el-menu-item index="/majorlist">专业管理</el-menu-item><el-menu-item index="/studentlist">学生管理</el-menu-item><el-menu-item index="1-3">学院管理</el-menu-item></el-menu-item-group></el-submenu></el-menu></el-aside><el-main><router-view></router-view></el-main></el-container></el-container></div>
</template><script>export default{data(){return{account:"",}},methods:{logout(){this.$confirm('您确定要退出么?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(() => {this.$message({type: 'success',message: '退出成功成功!'});sessionStorage.clear();this.$router.push("/login")})}},mounted(){this.account=window.sessionStorage.getItem("account");//将account存入路由器内部console.log(this.account);/* if(account==null){this.$router.push("/login")} */}}
</script><style>.el-header {background-color: #00a7fa;color: #333;line-height: 60px;}.header-title{width: 300px;float: left;text-align: left;font-size: 20px;color: white;}.el-main{background-color: aliceblue;height: 100vh;}
</style>

六、web前后端之间的会话跟踪

        当我们在登陆成功后需要进行其他操作时,前端会像后端发送http请求。但http请求是无状态的,因此后端不知道是谁在进行操作。对会话进行跟踪 就是为了解决这样的问题。

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

我们大致可以将会话跟踪描述为下图的过程

当我们在

具体实现

(1)在登陆成功后,在后端为用户生成一个token字符串

token令牌,可以理解为身份证号,是该用户唯一标识字符串,通过这个字符串进行前后端验证。

我们可以通过jwt组件为管理员生成token令牌

说明:

token令牌分为三部分:声明、载荷、签证

声明包含着生成类型和加密算法的基本信息,载荷包含着用户信息。二者通过base64转码生成,此过程可逆,不包含加密,因此不建议在载荷中加入用户关键信息。

签证结合前两部分以及密钥,加密生成,故密钥十分重要

我们通过maven添加依赖

<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.8.2</version></dependency>

并在服务器中注册(web.xml) 

 <!--验证token过滤器--><filter><filter-name>tokenfilter</filter-name><filter-class>com.wbc.dorm.filter.AdminTokenFilter</filter-class></filter><filter-mapping><filter-name>tokenfilter</filter-name><!--请求地址中有api地址的Servlet进入过滤器--><url-pattern>/api/*</url-pattern></filter-mapping>

由于登录界面不需要生成token,所以通过在其他配置信息的地址前添加/api来区分

将jwt的util稍作修改直接拿来用

package com.wbc.dorm.util;import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.wbc.dorm.model.Admin;import java.util.Date;
import java.util.HashMap;
import java.util.Map;/*** JWT工具类*/
public class JWTUtil {/*** 根据用户id,账号生成token* @param admin* @return*/public static String getToken(Admin admin) {String token = "";try {//过期时间 为1970.1.1 0:0:0 至 过期时间  当前的毫秒值 + 有效时间Date expireDate = new Date(new Date().getTime() + 10000*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)//设置token有效时间.sign(algorithm);//设置密钥}catch (Exception e){e.printStackTrace();return  null;}return token;}/*** 验证token是否有效* @param token* @return*/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部分数据,按需使用* @param token* @return*/public static DecodedJWT getTokenInfo(String token){return JWT.require(Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE")).build().verify(token);}}

添加Admin内的成员变量adminToken,并增加相应的get、set方法。

package com.wbc.dorm.model;public class Admin {private int id;private String account;private String password;private String adminToken;public String getAdminToken() {return adminToken;}public void setAdminToken(String adminToken) {this.adminToken = adminToken;}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;}@Overridepublic String toString() {return "Admin{" +"id=" + id +", account='" + account + '\'' +", password='" + password + '\'' +'}';}
}

并在登录servlet中添加相应代码以生成token

package com.wbc.dorm.web;import com.fasterxml.jackson.databind.ObjectMapper;
import com.wbc.dorm.dao.LoginDao;
import com.wbc.dorm.model.Admin;
import com.wbc.dorm.model.Result;
import com.wbc.dorm.util.JWTUtil;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;/*登录处理servlet*/
@WebServlet(urlPatterns = "/login",name = "login", loadOnStartup = 1)
public class LoginServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {/*登录请求的账号密码*/String account = req.getParameter("account");String password = req.getParameter("password");//System.out.println(account);//System.out.println(password);Result result = null;PrintWriter printWriter = resp.getWriter();/*调用dao层查询账号密码是否正确*/try {LoginDao loginDao = new LoginDao();Admin admin = loginDao.login(account, password);//System.out.println(admin.toString());if (admin!=null){//登陆成功后,为admin生成一个token字符串//使用jwt组件为管理员生成公私密钥String adminToken = JWTUtil.getToken(admin);//将adminToken放入adminadmin.setAdminToken(adminToken);//将admin放入标准化响应模型result = new Result(200, "登陆成功", admin);}else {result = new Result(201, "账号或密码错误", null);}} catch (Exception e) {e.printStackTrace();result = new Result(500, "系统忙", null);}ObjectMapper objectMapper = new ObjectMapper();String jsonString = objectMapper.writeValueAsString(result);printWriter.print(jsonString);}
}

(2)在浏览器中存储token,每次发送请求时将身份码一同发出

在登录时,接收后端相应处,添加接收身份码的代码,并将其添加到会话空间

sessionStorage.setItem("adminToken",resp.data.data.adminToken);

在我们操作发送请求时,如果在发送请求时,通过字符串拼接将身份码一同发于后端,实在过于麻烦,幸好axios为我们提供了请求拦截的功能

在main.js中添加如下代码,可以将身份码添加到请求头中,直接发送

//axios 请求拦截
axios.interceptors.request.use(config =>{//为请求头对象,添加Token验证的token字段config.headers.adminToken = window.sessionStorage.getItem('adminToken');return config;});

 (3)在后端创建一个过滤器,用于检测token的正确性,正确则退出过滤器继续响应,错误则直接向前端响应并给出状态码和提示信息

package com.wbc.dorm.filter;import com.fasterxml.jackson.databind.ObjectMapper;
import com.wbc.dorm.model.Result;
import com.wbc.dorm.util.JWTUtil;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.PrintWriter;public class AdminTokenFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {//父类向子类转换(强制类型转换)HttpServletRequest request = (HttpServletRequest) servletRequest;String adminToken = request.getHeader("adminToken");//验证tokenBoolean res= JWTUtil.verify(adminToken);System.out.println(res);//处理验证结果if(res){filterChain.doFilter(request, servletResponse);//离开过滤器继续向下执行}else{//向前端进行响应Result result = new Result(401,"token验证失败,请重新登录",null);PrintWriter printWriter = new PrintWriter(servletResponse.getWriter());printWriter.write(new ObjectMapper().writeValueAsString(result));}}
}

在前端中main.js中添加响应拦截器(类似于javaEE中的过滤器),用于拦截后端的身份码错误响应

 // 添加响应拦截器
axios.interceptors.response.use((resp) =>{//正常响应拦截if(resp.data.code==500){ElementUI.Message({message:resp.data.message,type:"error"});return;}if(resp.data.code==401){ElementUI.Message({message:resp.data.message,type:"error"});sessionStorage.clear();router.replace("/login");}return resp;});


http://www.mrgr.cn/news/1519.html

相关文章:

  • 前端宝典之六:React源码解析之lane模型
  • 数据库技术全景指南
  • Crypto++:系统架构和数据类型
  • CentOS 7 安装流程详细教程
  • 数据结构——排序(3):交换排序(续)
  • Windows有哪些免费好用的PDF编辑器推荐?
  • python 列表
  • 设计模式反模式:UML图示常见误用案例分析
  • 精益生产咨询:为企业量身定制的高效能蜕变计划!——张驰咨询
  • Element-plus el-input 添加图标
  • edge浏览器可以,chrome浏览器看不到接口数据
  • 昆仑万维推出全球首款 AI 短剧平台 SkyReels,「一人一剧」时代来临
  • LNMP 架构(Linux+NGINX+memcache+PHP)
  • Nginx 学习之 配置支持 IPV6 地址
  • 使用Apache POI和POI-OOXML实现word模板文档自动填充功能
  • redis——基本命令
  • 32-hashmap linkedmap treemap 的区别
  • STM32cubeMX配置Systick的bug
  • 机器学习--常见算法总结
  • 【MATLAB源码-第188期】基于matlab的64QAM系统相位偏移估计EOS算法仿真,对比补偿前后的星座图误码率。