• 管理系统搭建一般步骤(会话跟踪 路由导航守卫 响应拦截器)


    1,vue-cli进行项目搭建

    2,使用ELement-UI

    3,使用vue组件路由

    4,点击登录,向后端进行账号密码比对

    三种情况:

    密码有误

    服务器忙

    密码正确。

    具体步骤:

    首先写好前端一个大体框架,然后点击登录按钮时触发的函数,还有使用v-model对数据进行双向绑定。

    1. <template>
    2. <div class="login_container">
    3. <div class="login_box">
    4. <div class="img_box">
    5. <img src="./assets/logo.png" />
    6. div>
    7. <div style="padding: 100px 30px 30px 0px;">
    8. <el-form label-width="80px">
    9. <el-form-item label="账号">
    10. <el-input v-model="from.account">el-input>
    11. el-form-item>
    12. <el-form-item label="密码">
    13. <el-input type="password" v-model="from.password">el-input>
    14. el-form-item>
    15. <el-form-item>
    16. <el-button type="primary" round @click="login()">登录el-button>
    17. <el-button>取消el-button>
    18. el-form-item>
    19. el-form>
    20. div>
    21. div>
    22. div>
    23. template>

     其次就是js代码:这是vue框架所以格式是按照vue框架给定的来的。这段代码主要作用就是,前端传给后端账号密码后,后端给前端的响应,前端对响应做出判断,通过状态码做出判断 。

    由于后端 

    5,接下来就是后端:

     在写后端之前需要在数据库中创建一个管理员的表,代表登录人信息,因为后端需要和数据库进行比对,如果数据库中存在当前登录的信息才可以进入主页面。

    这是登录的servlet,读入流,调用dao的函数进行账号密码比对,通过比对结果向前端传入数据。

    由于json跨平台兼容,数据体积小等优点,所以我们通过json格式化java对象传给前端。 

    1. package com.ffyc.webserver.servlet;
    2. import com.fasterxml.jackson.databind.ObjectMapper;
    3. import com.ffyc.webserver.Models.CommonData;
    4. import com.ffyc.webserver.dao.LoginDao;
    5. import com.ffyc.webserver.Models.User;
    6. import com.ffyc.webserver.util.JWTUtil;
    7. import javax.servlet.ServletException;
    8. import javax.servlet.http.HttpServlet;
    9. import javax.servlet.http.HttpServletRequest;
    10. import javax.servlet.http.HttpServletResponse;
    11. import java.io.IOException;
    12. import java.io.PrintWriter;
    13. import java.sql.SQLException;
    14. public class LoginServlet extends HttpServlet {
    15. private LoginDao dao=new LoginDao();
    16. // @Override
    17. // protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    18. //
    19. // }
    20. // @Override
    21. // protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    22. // String account=req.getParameter("account");//接收请求中我们自己提交的数据
    23. // User user=new User("adele","123456789");
    24. // ObjectMapper objectMapper=new ObjectMapper();
    25. // String stu=objectMapper.writeValueAsString(user);
    26. // resp.getWriter().print(stu);
    27. // System.out.println(objectMapper.writeValueAsString(user));
    28. // if(account.equals("admin"))
    29. // {
    30. // resp.getWriter().print("账号已注册");
    31. // }
    32. // else
    33. // {
    34. // resp.getWriter().print("账号未注册");
    35. // }
    36. // }
    37. @Override
    38. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    39. String account=req.getParameter("account");
    40. String password=req.getParameter("password");
    41. System.out.println(account);
    42. System.out.println(password);
    43. PrintWriter printWriter=resp.getWriter();
    44. User user= null;
    45. CommonData commonData=null;
    46. try {
    47. user = dao.logindao(account,password);
    48. if(user==null) {
    49. commonData=new CommonData(201,"账号或密码输入有误,请重新输入!");
    50. }
    51. else {
    52. user.setToken(JWTUtil.getToken(user));
    53. commonData=new CommonData(200,user,"登录成功");
    54. System.out.println(user.getToken());
    55. }
    56. } catch (Exception throwables) {
    57. commonData=new CommonData(500,"服务器忙...请稍后重试!");
    58. }
    59. ObjectMapper objectMapper=new ObjectMapper();
    60. String json=objectMapper.writeValueAsString(commonData);
    61. printWriter.print(json);
    62. }
    63. }

    然后就是dao层的调用了,因为只要是对数据库进行增删改查操作时,我们都需要调用一次数据库,所以,将数据库连接打包成一个类,每当进行操作,调用该类即可。

    DBUtil.java

    1. package com.ffyc.webserver.util;
    2. import java.sql.*;
    3. public class DButils {
    4. static {
    5. try {
    6. Class.forName("com.mysql.cj.jdbc.Driver");
    7. } catch (ClassNotFoundException e) {
    8. e.printStackTrace();
    9. }
    10. }
    11. public static Connection getConnection() throws SQLException {
    12. String url = "jdbc:mysql://127.0.0.1:3306/userdb?serverTimezone=Asia/Shanghai";
    13. Connection connection = DriverManager.getConnection(url, "root", "123456");
    14. return connection;
    15. }
    16. public static void close(ResultSet resultSet, Statement statement, Connection connection) throws SQLException {
    17. if (resultSet != null) {
    18. resultSet.close();
    19. }
    20. if (statement != null) {
    21. statement.close();
    22. }
    23. if (connection != null) {
    24. connection.close();
    25. }
    26. }
    27. }

    然后再写一个登录对账号密码验证的dao:使用sql语言调用数据库进行查询,最后将查询到数据以对象形式返回。如果对象为空代表未在数据库查到当前账号,说明账号或密码有误,不为空则说明登录成功。

    1. package com.ffyc.webserver.dao;
    2. import com.ffyc.webserver.Models.User;
    3. import com.ffyc.webserver.util.DButils;
    4. import java.sql.*;
    5. public class LoginDao {
    6. public User logindao(String account, String password) throws SQLException {
    7. String sql="select * from user where account=? and password=?";
    8. Connection connection=DButils.getConnection();
    9. PreparedStatement preparedStatement= connection.prepareStatement(sql);
    10. preparedStatement.setObject(1,account);
    11. preparedStatement.setObject(2,password);
    12. ResultSet resultSet=preparedStatement.executeQuery();
    13. User user=null;
    14. if(resultSet.next())
    15. {
    16. user=new User();
    17. String psw=resultSet.getString("password");
    18. user.setAccount(account);
    19. user.setPassword(psw);
    20. }
    21. DButils.close(resultSet,preparedStatement,connection);
    22. return user;
    23. }
    24. }

     因为要以对象形式返回数据,所以需要创建登录者信息类:

    1. package com.ffyc.webserver.Models;
    2. public class User {
    3. String id;
    4. String account;
    5. String password;
    6. String token;
    7. public String getToken() {
    8. return this.token;
    9. }
    10. public void setToken(String token) {
    11. this.token = token;
    12. }
    13. public void setAccount(String account) {
    14. this.account = account;
    15. }
    16. public void setPassword(String password) {
    17. this.password = password;
    18. }
    19. public String getId() {
    20. return id;
    21. }
    22. public void setId(String id) {
    23. this.id = id;
    24. }
    25. public String getAccount() {
    26. return account;
    27. }
    28. public String getPassword() {
    29. return password;
    30. }
    31. }

    最后,不要忘记配置当前servlet:

    1. <servlet>
    2. <servlet-name>Loginservlet-name>
    3. <servlet-class>com.ffyc.webserver.servlet.LoginServletservlet-class>
    4. servlet>
    5. <servlet-mapping>
    6. <servlet-name>Loginservlet-name>
    7. <url-pattern>/LoginServleturl-pattern>
    8. servlet-mapping>

    这样,一个简单的登录就完成了。

    6,我们发现,类似于状态码为500的公共拦截码如果每次都写一遍很费时间,所以在前端为axios配置响应拦截器,将500这种公共拦截码进行拦截。

    1. import axios from 'axios';
    2. //设置访问后台服务器地址
    3. axios.defaults.baseURL = "http://localhost:8088/webServer/";
    4. //将 axios 挂载到 vue 全局对象中,使用 this 可以直接访问
    5. //axios 请求拦截 每次向后端发送
    6. axios.interceptors.request.use(config => {
    7. //为请求头对象,添加 Token 验证的 token 字段
    8. config.headers.userToken = window.sessionStorage.getItem('userToken');
    9. return config;
    10. })
    11. axios.interceptors.response.use((resp) => { //正常响应拦截
    12. if (resp.data.code == 500) {
    13. ElementUI.Message({
    14. message: resp.data.message,
    15. type: "error"
    16. })
    17. }
    18. if (resp.data.code == 402) {
    19. ElementUI.Message({
    20. message: resp.data.message,
    21. type: "error"
    22. })
    23. router.replace("/login");
    24. }
    25. return resp;
    26. });
    27. Vue.prototype.$http = axios;

     为axios框架配置响应拦截器,一旦后端做出响应,响应器拦截。

    7,存在的问题:在登录页面的地址栏输入主页面地址可以直接访问,无需验证账号密码。

    解决办法:在登录前判断验证用户是否登录,未登录就不能访问/main。

    路由导航守卫:每当发生路由,自动调用此方法。

            路由导航守卫是一种在路由导航过程中进行拦截和控制的机制。它允许开发者在用户导航到某个路由之前、之后或在路由发生变化时执行相应的操作。

            路由导航守卫可以用于实现诸如身份验证、权限检查、日志记录等功能。通过注册守卫函数,开发者可以在路由导航事件发生时介入,并根据需要执行特定的逻辑。

    to:访问组件地址

    from:从哪个组件发起的路由

    next():放行函数。 

    写入index.js中:导出路由器对象,并将代码包装在模块中并导出对象

    1. import Vue from 'vue';
    2. import router from 'vue-router'; /* 导入路由 */
    3. /* 导入需要路由的组件 */
    4. import Login from "../Login.vue";
    5. import Main from "../Main.vue";
    6. import student from '../student/student.vue';
    7. import marjor from '../marjor/marjor.vue';
    8. Vue.use(router)
    9. /* 定义组件路由 */
    10. var rout = new router({
    11. routes: [{
    12. path: '/',
    13. component: Login
    14. },
    15. {
    16. path: '/login',
    17. /* 地址 */
    18. name: 'login',
    19. /* 地址名 */
    20. component: Login /* 调用地址 */
    21. },
    22. {
    23. path: '/main',
    24. component: Main,
    25. children: [{
    26. path: "/student",
    27. component: student,
    28. }, {
    29. path: "/marjor",
    30. component: marjor
    31. }],
    32. }
    33. ]
    34. });
    35. //导出路由对象
    36. rout.beforeEach((to, from, next) => {
    37. if (to.path == '/login') {
    38. //如果用户访问的登录页, 直接放行
    39. return next();
    40. } else {
    41. var account = window.sessionStorage.getItem("account");
    42. if (account == null) {
    43. return next("/login");
    44. } else {
    45. next();
    46. }
    47. }
    48. })
    49. export default rout;

    8, 前端显示用户信息,并实现安全退出。

    9,登录成功后,进入管理后台,需要会话跟踪。

    由于http请求是无状态的,默认不携带用户信息,所以在一次会话中,需要多次向后端发送请求,我们需要让后端程序知道是谁发送的请求。这个过程称为会话跟踪。 

     ①传统的session会话:登录成功后,在后端生成HttpSession对象。里面可以存储用户信息和id,存储在服务器端,id号响应给前端,前端提交id到后端,查询session对象。(不适合分布式架构)

             在分布式架构中,各个子系统通常相互独立且自治,它们可以并行地处理任务,从而提高系统的处理能力。每个子系统可以运行在自己的硬件设备上,也可以共享资源,例如数据库、存储等。通过将负载均衡和故障容错机制引入到系统中,分布式架构可以实现系统的高可用性和容错性。

     ②token会话:

    jwt搭建:

    ①配置安装jar包;

    ②创建生成token方法;

    ③之后每次请求都要发送token(在前端axios中配置拦截器,每次向后端发送) 

    1. //axios 请求拦截 每次向后端发送
    2. axios.interceptors.request.use(config => {
    3. //为请求头对象,添加 Token 验证的 token 字段
    4. config.headers.userToken = window.sessionStorage.getItem('userToken');
    5. return config;
    6. })

    ④每次做出响应之前进行token验证

    1. package com.ffyc.webserver.util;
    2. import com.auth0.jwt.JWT;
    3. import com.auth0.jwt.JWTVerifier;
    4. import com.auth0.jwt.algorithms.Algorithm;
    5. import com.auth0.jwt.interfaces.DecodedJWT;
    6. import com.ffyc.webserver.Models.User;
    7. import java.util.Date;
    8. import java.util.HashMap;
    9. import java.util.Map;
    10. /**
    11. * JWT工具类
    12. */
    13. public class JWTUtil {
    14. /**
    15. * 根据用户id,账号生成token
    16. * @param u
    17. * @return
    18. */
    19. public static String getToken(User u) {
    20. String token = "";
    21. try {
    22. //过期时间 为1970.1.1 0:0:0 至 过期时间 当前的毫秒值 + 有效时间
    23. Date expireDate = new Date(new Date().getTime() + 3600*1000);
    24. //秘钥及加密算法
    25. Algorithm algorithm = Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE");
    26. //设置头部信息
    27. Map header = new HashMap<>();
    28. header.put("typ","JWT");
    29. header.put("alg","HS256");
    30. //携带id,账号信息,生成签名
    31. token = JWT.create()
    32. .withHeader(header)
    33. .withClaim("id",u.getId())
    34. .withClaim("account",u.getAccount())
    35. .withExpiresAt(expireDate)
    36. .sign(algorithm);
    37. }catch (Exception e){
    38. e.printStackTrace();
    39. return null;
    40. }
    41. return token;
    42. }
    43. /**
    44. * 验证token是否有效
    45. * @param token
    46. * @return
    47. */
    48. public static boolean verify(String token){
    49. try {
    50. //验签
    51. Algorithm algorithm = Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE");
    52. JWTVerifier verifier = JWT.require(algorithm).build();
    53. DecodedJWT jwt = verifier.verify(token);
    54. return true;
    55. } catch (Exception e) {//当传过来的token如果有问题,抛出异常
    56. return false;
    57. }
    58. }
    59. /**
    60. * 获得token 中playload部分数据,按需使用
    61. * @param token
    62. * @return
    63. */
    64. public static DecodedJWT getTokenInfo(String token){
    65. return JWT.require(Algorithm.HMAC256("ZCEQIUBFKSJBFJH2020BQWE")).build().verify(token);
    66. }
    67. }
    1. package com.ffyc.webserver.filter;
    2. import com.fasterxml.jackson.databind.ObjectMapper;
    3. import com.ffyc.webserver.Models.CommonData;
    4. import com.ffyc.webserver.util.JWTUtil;
    5. import javax.servlet.*;
    6. import javax.servlet.http.HttpServletRequest;
    7. import java.io.IOException;
    8. import java.io.PrintWriter;
    9. public class TokenFilter implements Filter {
    10. @Override
    11. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    12. HttpServletRequest httpServletRequest=(HttpServletRequest) servletRequest;
    13. String userToken = httpServletRequest.getHeader("userToken");
    14. if(JWTUtil.verify(userToken))
    15. {
    16. filterChain.doFilter(servletRequest, servletResponse);
    17. }else{
    18. PrintWriter printWriter=servletResponse.getWriter();
    19. CommonData commonData=new CommonData(402,"Token认证失败!");
    20. ObjectMapper objectMapper=new ObjectMapper();
    21. String json=objectMapper.writeValueAsString(commonData);
    22. printWriter.print(json);
    23. }
    24. }
    25. }

    会话跟踪实现:

    第一次登录成功时,在后端根据管理员信息以及密钥生成的token(字符串),响应给前端,之后每次请求都携带token到后端,后端对token进行验证。 

  • 相关阅读:
    前端进阶-js查漏补缺
    Unity --- 导航网格 与 导航的使用
    1、CsvHelper使用小记一
    qt pdf 模块简介
    策略模式(Stragedy)
    UE4 钥匙开门
    2023年中国精准PCI行业发展规模及发展趋势分析:精准PCI日益普及[图]
    2020同济大学电子与信息工程学院计算机系夏令营机试题目【含题解、注释】
    并发编程day03
    京东AB主图测试实验,优化主图提升转化!
  • 原文地址:https://blog.csdn.net/m0_71385141/article/details/133841925