1. 用户登录
1.1 用户登录操作
1.1.1 编辑UserController
package com.jt.controller;
import com.jt.pojo.User;
import com.jt.service.UserService;
import com.jt.vo.SysResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @author 刘昱江
* 时间 2021/5/11
*/
@RestController
@CrossOrigin
@RequestMapping("/user") //抽取公共的请求
public class UserController {
@Autowired
private UserService userService;
/**
* 1.url地址: /user/login
* 2.请求参数: 用户表单对象的JSON串 post类型
* 3.返回值结果 SysResult token?有值 正确 null 错误
*/
@PostMapping("/login")
public SysResult login(@RequestBody User user){
String token = userService.findUserByUP(user);
if(StringUtils.hasLength(token)){
return SysResult.success(token);
}else{
return SysResult.fail();
}
}
}
1.1.2 编辑UserService
/**
* user对象: username/password 明文
* 业务思路:
* 1.将密码进行加密处理 md5加密方式
* 2.根据新的用户名和密码查询数据
* 3. 结果:null 没查到 u/p错误
* 不为null u/p正确 返回令牌 token UUID算法
*
* * @param user
* @return
*
* 数据库信息: 用户名 :admin 密码:admin123456
*/
@Override
public String findUserByUP(User user) {
//1.将密码加密 一般可能添加 盐值: 由公司域名构成
// hash(md5(www.baidu.com12345))
String md5Pass =DigestUtils
.md5DigestAsHex(user.getPassword().getBytes());
//2.根据用户名/密码查询数据库.
user.setPassword(md5Pass);
//根据对象中不为null的属性当做where条件
QueryWrapper queryWrapper = new QueryWrapper(user);
User userDB = userMapper.selectOne(queryWrapper);
//3.返回密钥token
String token = UUID.randomUUID().toString()
.replace("-", "");
return userDB==null?null:token;
}
1.2 全局异常的处理
1.2.1 业务说明
在后端服务器中会有很多的地方需要添加,如果都写到业务代码中,会造成代码可读性差 如图:
1.2.2 编辑全局异常处理
@RestControllerAdvice //定义全局异常处理 拦截controller层 返回JSON
public class AOPException {
//实现原理 异常通知
//1.拦截什么类型的异常!!
//2.拦截之后如何处理!!
@ExceptionHandler({RuntimeException.class})
public Object exception(Exception e){
//将异常 控制台输出
e.printStackTrace();
return SysResult.fail();
}
}
1.3 保存用户登录信息
1.3.1 下载vue文件
说明: 去码云中的资源文件中查找组件Home/Welcome.导入项目中
1.3.2 实现路由跳转
说明: 实现Home组件路由的跳转 代码如下
页面效果:
1.3.3 业务需求
当用户登录成功之后,需要重定向到home页面.这时用户token将会丢失.用户后端的操作要求用户必须登录之后才可以执行,因此必须保存用户的登录凭证 token!!!
问题: 浏览器如果保存服务器记录?
补充知识:
1.cookie 可以将用户信息永久的保存到cookie中. 有安全性问题 如果需要使用则需要特殊处理 IP绑定/手机的认证 30天免密登录 视频会员
2.Session 表示服务器的会话机制 可以将数据保存到Session中.当会话关闭时Session失效了.
1.3.4 用户登录凭证保存
当用户登录成功之后,需要通过session保存用户的token信息.代码如下:
1.4 路由导航守卫
1.4.1 业务介绍
要求: 如果用户没有登录(token),则不允许访问其他页面.
判断依据: 根据session中的token进行判断. token有数据 则放行请求 没有数据 跳转到登录页面.
难点: 如果实现请求的拦截?
实现策略: 使用路由导航守卫.
1.4.2 路由导航守卫API
编辑router中的index.js 实现功能
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../components/Login.vue'
import Home from '../components/Home.vue'
Vue.use(VueRouter)
const routes = [
{path: '/', redirect: '/login'},
{path: '/login', component: Login},
{path: '/home', component: Home}
]
//1.定义路由对象
const router = new VueRouter({
routes
})
// 2.定义导航守卫(注意位置)
// beforeEach: 循环遍历用户的所有的请求.(拦截)
// 在其中需要定义一个回调函数(3个参数)
// 参数介绍: to :要访问的请求路径
// from: 从哪个页面跳转而来
// next: 表示请求放行
//业务需求: 1.如果用户请求/login
// 2.如果不是login则判断是否登录 token
router.beforeEach((to,from,next) => {
if(to.path === '/login') return next()
//2.获取token信息
let token = window.sessionStorage.getItem('token')
//3.判断token是否有数据?? if(token) 如果token不为null
//如果token为null 则跳转到登录页面
if(!token) return next('/login')
//表示放行
next()
})
export default router
1.5 实现用户退出操作
1.5.1 需求说明
当用户点击退出按钮时,应该删除session中的数据. 之后跳转到登录页面.
1.5.2 编辑页面JS
1.6 elementUI 页面布局说明
1.6.1 布局API
<el-container>
<el-header>Header</el-header>
<el-container>
<el-aside width="200px">Aside</el-aside>
<el-main>Main</el-main>
</el-container>
</el-container>
1.6.2 页面布局
2. 左侧菜单实现
2.1 业务分析
说明: 当用户跳转到home页面中,之后发起Ajax请求获取模块信息.之后通过导航菜单的形式进行展现.
2.2 业务接口说明
2.3 实现权限获取后端业务
2.3.1 表结构说明
说明: 权限有父子级关系,通过parent_id实现父子关联
2.3.2 构建权限代码结构
2.3.3 权限POJO对象
2.3.4 编辑RightsController
package com.jt.controller;
import com.jt.pojo.Rights;
import com.jt.service.RightsService;
import com.jt.vo.SysResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@CrossOrigin
@RequestMapping("/rights")
public class RightsController {
@Autowired
private RightsService rightsService;
/**
* URL: /rights/getRightsList
* 参数: null
* 类型: get
* 返回值: SysResult对象 List
* 业务: 只查询前 2级权限
*/
@GetMapping("/getRightsList")
public SysResult getRightsList(){
List<Rights> rightsList =
rightsService.findRightsList();
return SysResult.success(rightsList);
}
}
2.3.5 编辑RightsService
package com.jt.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jt.mapper.RightsMapper;
import com.jt.pojo.Rights;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class RightsServiceImpl implements RightsService{
@Autowired
private RightsMapper rightsMapper;
/**
* 查询一级/二级菜单
* 查询条件: 一级菜单 parent_id = 0
* 二级菜单 parent_id = 一级的Id
* 作业: 利用左连接的方式 实现数据的封装 restMap
* @return
*/
@Override
public List<Rights> findRightsList() {
//1.查询一级菜单数据
QueryWrapper<Rights> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("parent_id",0);
List<Rights> oneList =
rightsMapper.selectList(queryWrapper);
//2.如何查询二级菜单 父子关系的封装!!!
for (Rights oneRights : oneList){
//查询该元素的二级菜单
//QueryWrapper<Rights> queryWrapper2 = new QueryWrapper<>();
queryWrapper.clear();
queryWrapper.eq("parent_id",oneRights.getId());
List<Rights> twoList = rightsMapper.selectList(queryWrapper);
oneRights.setChildren(twoList);
}
return oneList;
}
}
2.4 实现前端左侧菜单展现
2.5 左侧菜单ElementUI学习
<el-container>
<!-- 当打开左侧菜单时 宽度为200, 当不打开时为默认值-->
<el-aside :width="isCollapse ? '64px' : '200px' ">
<!-- 这是左侧菜单-->
<!--定义折叠项-->
<div class="leftCollapse" @click="collspseClick">|||</div>
<!--
1.background-color="#2C3E50"
2.text-color="#fff" 文本颜色
3.active-text-color="#4094ff"
4.unique-opened=true 是否只打开一个菜单
5.collapse 是否折叠
6.collapse-transition 折叠时是否展现动画效果
7.default-active 默认选中的菜单
-->
<el-menu background-color="#2C3E50" text-color="#fff"
active-text-color="#4094ff"
unique-opened
:collapse="isCollapse" :collapse-transition="isCollapseTransition" router :default-active="defaultActive">
<!-- 定义一级菜单 -->
<el-submenu :index="menu.id+''" v-for="menu in menuList" :key="menu.id">
<!-- 定义一级菜单模版 -->
<template slot="title">
<!-- 定义左侧图标-->
<i :class="menuIcon[menu.id]"></i>
<!-- 定义菜单名称-->
<span>{{menu.name}}</span>
</template>
<!-- 定义二级菜单 -->
<el-menu-item :index="childrenMenu.path" v-for="childrenMenu in menu.children" :key="childrenMenu.id"
@click="defaultActiveMenu(childrenMenu.path)">
<template slot="title">
<i class="el-icon-menu"></i>
<span>{{childrenMenu.name}}</span>
</template>
</el-menu-item>
</el-submenu>
</el-menu>
</el-aside>
<!-- 定义主页面结构-->
<el-main>
<!-- 定义路由展现页面-->
<router-view></router-view>
</el-main>
</el-container>
2.6 脚手架项目执行过程
2.6.1 目录结构的说明
2.6.2 梳理程序加载流程
2.7 Home页面路由说明
2.7.1 中间菜单的结构
将来在view的位置,展现其他的页面组件
当前组件是谁: Home组件
如果需要在该位置展现组件,则必然是Home组件的子级
2.7.2 访问主页面
用户跳转到home组件之后,在中间展现欢迎页面.配置如下
页面效果展现: