登录功能的实现---(前后端分离)

一、登录与主界面

1.新建前端项目

2.配置组件路由

npm i vue-router@3.5.3

src下创建 router文件夹 ,并在router下创建index.js 文件

同时在src下创建两个组件 Login.vue和Main.vue

在index.js中配置 上面两个组件

 /* 导入路由 */
import Vue from 'vue';
import router from 'vue-router';

/* 导入自己的组件 */
import Login from "../Login.vue"; 
import Main from "../Main.vue"; //  ../上一级目录中找

Vue.use(router) //vue支持使用router组件

/* 定义组件路由 */
var rout = new router({  //新建router对象
    routes: [   //组件映射
        {
            path: '/login',  //为组件定义地址
            name: 'name',	 //组件起名  可不写
            component: Login //对应那个组件
        },
        {
            path: '/main',
            component: Main
        }
    ]
});
//导出路由对象 
export default rout;

main.js中配置

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

import router from './router/index.js';
Vue.use(router);

new Vue({
  render: h => h(App),
  router,
}).$mount('#app')


app.vue中<-router-view>标签

可以将不需要的内容删去

<template>
  <div id="app">
      <!-- 在此标签上,用来切换不同的组件 -->
   <router-view>< /router-view>
  </div>
</template>

<script>
</script>

<style>
</style>

3.导入elementUI

main.js中配置elementUI

将此段代码加入,与导入组件路由相同

//导入 element mui
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);

使用elementUI中的代码美化 登录和主界面

登录

 <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">
 					<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="primary" @click="login()">登录</el-button>
 						<el-button>取消</el-button>
 					</el-form-item>
 				</el-form>
 			</div>
 		</div>
 	</div>
 </template>

<script>
	export default { //导出组件
		data() {
			return {
				form: {
					account: "admin",
					password: "111"
				}
			}
		},
		methods: {
			login() {
				
				//   后端进行一次交互的位置
				//路由跳转
				this.$router.push("/main");

			}
		}
	}
	
	
	//将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/R-C.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> 

main

<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-s-custom" style="margin-right: 15px">
					<span>{{account}}</span>
				</i>
				<el-dropdown-menu slot="dropdown">
					<el-dropdown-item>修改密码</el-dropdown-item>
					<el-dropdown-item><span @click="">安全退出</span></el-dropdown-item>
					<el-dropdown-item><span @click="">测试</span></el-dropdown-item>
				</el-dropdown-menu>
			</el-dropdown>
		</el-header>
		<el-container>
			<!-- 左边-->
			<el-aside width="200px">
				<el-menu :default-openeds="['1', '3']" router>
					<el-submenu index="1">
						<template slot="title"><i class="el-icon-message"></i>操作菜单</template>
						<el-menu-item>宿舍管理</el-menu-item>
						<el-menu-item>宿管员管理</el-menu-item>
					</el-submenu>
				</el-menu>
			</el-aside>
			<!-- 工作区-->

			<el-main>

			</el-main>

		</el-container>
	</el-container>
</template>

<script>
</script>

<style>
	.el-header {
		background-color: #55aaff;
		color: #333;
		line-height: 60px;
	}

	.el-aside {
		background-color: #D3DCE6;
		color: #333;
		text-align: center;
		height: 90vh;
	}

	.el-main {
		background-color: #E9EEF3;
		color: #333;
		/* text-align: center; */
		height: 90vh;
	}
</style>

此时两个主要界面设置完成

二、登录操作

1.前端发送请求到后端

1.1安装axios

与导入elementUi方法相同

npm install axios

端口要与后端的不同

//导入网络请求库
import axios from 'axios';
axios.defaults.baseURL="http://127.0.0.1:8088/dormms/";
Vue.prototype.$http=axios;

1.2像后端发送请求

在Login中加入函数 并接收后端返回的账号 与 token

 			login() {
 				//   后端进行一次交互的位置
 				//console.log(this.form)
 				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);
 
 					}
 
 				})
 				//login为后端servlet的地址
 			}

在main中加入函数 拿到响应回来的账号并显示

	export default {
		data() {
			return {
				account: ""
			}
		},
		methods: {



		},
		mounted() {
			this.account = sessionStorage.getItem("account");
			
		}
	}

2.后端处理

此时后端javaEE以搭建完成,所用到的jar包以全部导入

以及创建了一个类LoginServlet来接收请求,并在web.xml文件中配置LoginServlet

2.1后端接收请求

public class LoginServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter printWriter= resp.getWriter();
        CommonResult commonResult=null;
        String account=req.getParameter("account");
        String password=req.getParameter("password");
        LoginDao loginDao=new LoginDao();
        try {
            Admin admin=loginDao.login(account,password);
            if(admin!=null){
                String token= JWTUtil.getToken(admin);
                admin.setToken(token);//token赋给admin
              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);
    }

}

2.2连接数据库

创建数据库   并加入一条管理员数据

CREATE DATABASE dorm_db CHARSET utf8

CREATE TABLE admin(
	id INT PRIMARY KEY AUTO_INCREMENT,
	account VARCHAR(20),
	PASSWORD VARCHAR(20)
)

连接jdbc

public class LoginDao {

    public Admin login(String account,String password) throws ClassNotFoundException, SQLException {
        Admin admin=null;
        Connection connection=null;
        PreparedStatement ps=null;

        try{
            Class.forName("com.mysql.cj.jdbc.Driver");
            //动态 加载Driver类
            String url="jdbc:mysql://127.0.0.1:3306/dorm_db?serverTimezone=Asia/Shanghai";
            String uname="root";
            String pwd="root";
            connection= DriverManager.getConnection(url,uname,pwd);

            ps= connection.prepareStatement("SELECT * FROM admin WHERE account=? AND PASSWORD=?");
            ps.setObject(1,account);
            ps.setObject(2,password);
            ResultSet res=ps.executeQuery();
            while (res.next()){
                admin=new Admin();
                admin.setId(res.getInt("id"));
                admin.setAccount(res.getString("account"));
            }
        }finally {
            if(connection!=null){
                connection.close();
            }
            if(ps!=null){
                ps.close();
            }
        }
        return admin;
    }
}

2.3数据封装

自定义状态码

创建CommonResult类 code data message

LoginServlet 接收到dao传回的数据, (token)

将状态码,admin对象,返回的信息通过 commonResult封装,已json格式响应到前端

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;
    }
}

管理员类

public class Admin {

    private int id;
    private String account;
    private String password;
    private String 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;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }
}

3.验证登录

为了避免直接通过网址来进入系统界面,

加入路由导航守卫,不允许直接通过网址进入main

index.js

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

4.会话跟踪

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

http请求是无状态的,只有用户发出请求时服务器才会响应,客户端与服务端之间的联系是离散的、非连续的;如果用户想在同一个网站的多个页面之间转换时,无法确定是否是同一个用户;对会话进行跟踪就是为了解决这样的问题。

向后端发送账号密码,与数据库连接验证成功后,在后端生成一个token(令牌 唯一的),把token响应给前端

4.1生成token

/**
 * JWT工具类
 */
public class JWTUtil {

    /**
     * 根据用户id,账号生成token
     * @param
     * @return
     */
    public static String getToken(Admin admin) {
        String token = "";
        try {
            //过期时间 为1970.1.1 0:0:0 至 过期时间  当前的毫秒值 + 有效时间
            Date expireDate = new Date(new Date().getTime() + 30*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是否有效
     * @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);
    }
}

此时通过loginservelt

            if(admin!=null){
                String token= JWTUtil.getToken(admin);
                admin.setToken(token);//token赋给admin

4.2前端存储token

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

4.3将token携带向后端发送

之后的每次请求,都将token携带着向后端发送

main.js中 将token发送给后端

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

4.4后端token进行解析验证

后端的java对请求中的token进行解析验证

添加token过滤器

JWTUtil.verify(adminToken); verify验证token的方法

验证成功–>过滤器向后执行

验证失败–>将错误信息封装到工具类中

public class AdminTokenFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        //System.out.println("token过滤验证");
        //接收请求头中的 adminToken
        HttpServletRequest request=(HttpServletRequest)servletRequest;
        String adminToken=request.getHeader("adminToken");
        boolean verify= JWTUtil.verify(adminToken);
        //System.out.println(verify+"  过滤器验证token");
        if(verify){
            //验证成功 继续向后执行
            filterChain.doFilter(servletRequest, servletResponse);
        }else {
            //验证失败
            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);
        }
    }
}

配置token过滤器

    <filter>
        <filter-name>AdminTokenFilter</filter-name>
        <filter-class>com.ffyc.dorm.filter.AdminTokenFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AdminTokenFilter</filter-name>
        <url-pattern>/back/*</url-pattern>
    </filter-mapping>

5.响应拦截

由于后端对前端的响应,401与500较为常见,我们不必对每次响应回的401 500进行if处理

如:

if(resp.data.code==201){
		this.$message({message: resp.data.message,type: 'warning' });			
}

main.js

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

6.过滤器

解决异步请求过滤器

public class CorsFilter implements Filter {


    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        //允许携带Cookie时不能设置为* 否则前端报错
        httpResponse.setHeader("Access-Control-Allow-Origin", httpRequest.getHeader("origin"));//允许所有请求跨域
        httpResponse.setHeader("Access-Control-Allow-Methods", "*");//允许跨域的请求方法GET, POST, HEAD 等
        httpResponse.setHeader("Access-Control-Allow-Headers", "*");//允许跨域的请求头
        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");//是否携带cookie

        filterChain.doFilter(servletRequest, servletResponse);
    }
}

设置字符过滤器

public class EncodFilter implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletRequest.setCharacterEncoding("utf-8");
        servletResponse.setCharacterEncoding("utf-8");
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

xml配置

在开始登录时提交表单此时scessionstorage里面还没有信息

并且在登录时也不需要验证token,所以当前端请求得sevlet地址为login时,不需要通过token过滤器。
所以在配置其他访问地址时(除过登录)前面可以加上/back/servlet地址,表示登录之后的访问
 

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">


    <servlet>
        <servlet-name>login11</servlet-name>
        <servlet-class>com.ffyc.dorm.servlet.LoginServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>login11</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>


    <filter>
        <filter-name>filterlogin</filter-name>
        <filter-class>com.ffyc.dorm.filter.EncodFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>filterlogin</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>filterco</filter-name>
        <filter-class>com.ffyc.dorm.filter.CorsFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>filterco</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


    <filter>
        <filter-name>AdminTokenFilter</filter-name>
        <filter-class>com.ffyc.dorm.filter.AdminTokenFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AdminTokenFilter</filter-name>
        <url-pattern>/back/*</url-pattern>
    </filter-mapping>
</web-app>

7.简单封装jdbc代码

新建工具类,处理重复代码

public class JdbcUtil {


    static String url="jdbc:mysql://127.0.0.1:3306/dorm_db?serverTimezone=Asia/Shanghai";
    static String uname="root";
    static String pwd="root";

    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    //获取数据库连接对象
    public static Connection getConnection() throws SQLException {
        Connection connection= DriverManager.getConnection(url,uname,pwd);
        return connection;
    }

    //
    public  static  void close(Connection connection, PreparedStatement preparedStatement) throws SQLException {
        if(connection!=null){
            connection.close();
        }
        if(preparedStatement!=null){
            preparedStatement.close();
        }
    }
}

此时logindao

public class LoginDao {

    public Admin login(String account,String password) throws ClassNotFoundException, SQLException {
        Admin admin=null;
        Connection connection=null;
        PreparedStatement ps=null;

        try{
            connection = JdbcUtil.getConnection();
            ps= connection.prepareStatement("SELECT * FROM admin WHERE account=? AND PASSWORD=?");
            ps.setObject(1,account);
            ps.setObject(2,password);
            ResultSet res=ps.executeQuery();
            while (res.next()){
                admin=new Admin();
                admin.setId(res.getInt("id"));
                admin.setAccount(res.getString("account"));
            }
        }finally {
            JdbcUtil.close(connection,ps);
        }
        return admin;
    }
}

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值