项目计划
- 完成登录功能 同步请求方式 密码加密 注销功能
第一章 登录功能开发
1.1 页面导入步骤
1.1.1 拷贝静态资源,放main工程webapp/static目录下
1.1.2 创建登录页面
1.1.3 相对路径,绝对路径问题
- 绝对路径
不可改变的路径称之为绝对路径
本地路径:以盘符开始的路径称之为绝对路径, c:/test/test.html
网络路径:以协议,域名,端口号所组合的路径为绝对路径
http://localhost:8080/atcrowdfunding/image/xxx.jpg
http:// www.xxx.com/test/test.com
- 相对路径:
可以改变的路径
默认的相对路径需要一个参考的位置,取值为当前资源的访问路径
- 路径以斜杠开头
路径以斜杠开头,也是相对路径,但是参考的位置和默认的相对路径不一样
- 前台路径(img, css, js, form)
参考的位置是服务器的根路径
response.sendRedirect(request.getContextPath()+"/xxx.jsp");
- 后台路径( java, xml )
参考的位置是web应用的根路径
request.getRequestDispatcher("/xxx.jsp").forward(req,resp);
1.2 项目启动监听器
1.2.1 WEB监听器
- 监听当前应用ServletContext对象
javax.servlet.ServletContextListener
监听ServletContext对象创建和销毁.
应用场景:
在服务器启动时建立数据库表结构,初始化数据库.
在服务器启动时,将数据库常量数据加载到内存,提供访问效率.
在服务器启动时,获取项目上下文路径,存放到application域,给页面使用.
存放计数器,计算在线用户数.
javax.servlet.ServletContextAttributeListener
监听ServletContext对象的属性的变化:添加,覆盖,删除 - 监听器HttpSession对象
javax.servlet.http.HttpSessionListener
监听HttpSession对象的创建和销毁
javax.servlet.http.HttpSessionAttributeListener
监听HttpSession对象的属性变化:添加,覆盖,删除 - 监听HttpServletRequest对象
javax.servlet.ServletRequestListener
监听HttpServletRequest对象的创建和销毁
javax.servlet.ServletRequestAttributeListener
监听HttpServletRequest对象的属性变化:添加,覆盖,删除
1.2.2 自定义监听器
- 在服务器启动时,将应用上下文路径存放到application域中
- 在JSP页面中使用EL表示式获取应用上下文路径使用它.
- 编写监听器
package com.atguigu.atcrowdfunding.listener;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.atguigu.atcrowdfunding.constant.AppConst;
/**
* 应用启动监听器
* 应用启动的时候保存一些共享的数据信息
*/
public class AppStartUpListener implements ServletContextListener {
Logger logger = LoggerFactory.getLogger(getClass());
/**
* 项目启动调用
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext servletContext = sce.getServletContext();
String contextPath = servletContext.getContextPath();// /atcrowdfunding-main
//保存项目路径
servletContext.setAttribute(AppConst.APP_PATH, contextPath);
logger.info("项目启动完成....");
}
/**
* 项目停止调用
*/
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
1.2.3 注册到web.xml中
<!--项目启动监听器 -->
<listener>
<listener-class>com.atguigu.atcrowdfunding.listener.AppStartUpListener</listener-class>
</listener>
1.3 前端流程业务
设计 首页
设计 登录页面
设计 管理员登录后主页面
设计 会员登录后主页面
1.3.5 登录涉及表
- t_admin 管理员
- t_member 会员表
1.3.6 流程图
用户登录时序图
1.4 抽取公共菜单
1.4.1 完成登录控制流转
/**
* 处理登录请求
*/
@Controller
public class LoginController {
Logger logger = LoggerFactory.getLogger(getClass());
@RequestMapping("/login")
public String login(TAdmin admin,HttpSession session,Model model){
//1、进行登录
logger.debug("用户进行登录:用户名:{} 密码: {}",admin.getLoginacct(),admin.getUserpswd());
return "main";
}
}
1.4.2 抽取页面side-bar.jsp
/WEB-INF/jsp/common/side-bar.jsp
1.4.3 包含菜单页面
<%@include file="/WEB-INF/jsp/common/menu.jsp" %>
1.4.4 静态包含和动态包含区别
-
静态包含
将两个页面合并一个页面,编译生成一个类;
<%@ include file="/WEB-INF/jsp/common/menu.jsp "%> -
动态包含
生成多个类
<jsp:include page="/WEB-INF/jsp/common/menu.jsp "></jsp:include>
1.5 表单重复提交-重定向
完成登录控制流转
/**
* 处理登录请求
*/
@Controller
public class LoginController {
Logger logger = LoggerFactory.getLogger(getClass());
@RequestMapping("/main ")
public String main(HttpSession session){
return "main";
}
@RequestMapping("/login")
public String login(TAdmin admin,HttpSession session,Model model){
//1、进行登录
logger.debug("用户进行登录:用户名:{} 密码:{}",admin.getLoginacct(),admin.getUserpswd());
//2、登录完成跳转到main页面。重定向到main?
//转发能访问受保护的资源,重定向不能;
return "redirect:/main ";
}
}
1.6 基本逻辑开发
- 增加index.jsp
- 增加index.jsp到login.jsp页面的跳转
- 修改登录页面login.jsp的表单及验证表单数据,提交请求
- 处理登录请求
- 处理器类(TAdminController),处理请求方式,根据登录进行判断并跳转页面
- 业务层接口(TAdminService)和实现类(TAdminServiceImpl)
- DAO接口(TAdminMapper )和映射(TAdminMapper.xml)
- 创建TAdmin/TMember实体类
- main.jsp页面
1.7 加密与解密
1.7.1 密文
1.7.2 加密算法分类
1.7.2.1 对称加密
常见加密算法
DES : Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,1977年被美国联邦政府的国家标准局确定为联邦资料处理标准(FIPS),并授权在非密级政府通信中使用,随后该算法在国际上广泛流传开来。
AES : Advanced Encryption Standard, 高级加密标准 .在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。
特点:
加密速度快, 可以加密大文件
密文可逆, 一旦密钥文件泄漏, 就会导致数据暴露
加密后编码表找不到对应字符, 出现乱码
一般结合Base64使用
1.7.2.2 非对称加密
RSA
1.7.2.3 不可逆加密
MD5算法具有以下特点:
1、压缩性:任意长度的数据,算出的MD5值长度都是固定的。(32位长度字符串)
2、容易计算:从原数据计算出MD5值很容易。(不可逆算法)
3、抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
4、强抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的
MD5加密后的内容可以通过暴力破解(网站当中使用验证码,目的就是为了防止暴力破解)
消息摘要
消息摘要(Message Digest)又称为数字摘要(Digital Digest)
它是一个唯一对应一个消息或文本的固定长度的值,它由一个单向Hash加密函数对消息进行作用而产生
特点
无论输入的消息有多长,计算出来的消息摘要的长度总是固定的。例如应用MD5算法摘要的消息有128个比特位,用SHA-1算法摘要的消息最终有160比特位的输出
只要输入的消息不同,对其进行摘要以后产生的摘要消息也必不相同;但相同的输入必会产生相同的输出
消息摘要是单向、不可逆的
常见算法 :
MD5
SHA1
SHA256
SHA512
彩虹表:穷举;
1.7.3 常用加解密算法
Base64位加密(可加密解密)
MD5加密(加密不可逆)
SHA 1加密(加密不可逆)
AES加密(需要密钥才能解密)
RSA加密(公钥加密,私钥解密)
1.8 登录完成
1.8.1 登录成功
展示session用户信息
1.8.2 登录失败
1.8.2.1 展示错误消息
@RequestMapping("/login")
public String login(TAdmin admin,HttpSession session,Model model){
//1、进行登录
logger.debug("用户进行登录:用户名:{} 密码: {}",admin.getLoginacct(),admin.getUserpswd());
//2、调用业务进行登录,成功返回对象,失败返回null
TAdmin loginUser = adminUserService.login(admin);
if(loginUser == null){
logger.error("登录失败:用户名:{}",admin.getLoginacct());
//失败了去登录页面重新登录;带了forward:、redirect:前缀的视图解析器不拼串
model.addAttribute(AppConst.MSG, "账号或密码错误");
//参数类型是一个对象;
//1)、自动将页面传来的值和对象里面的属性一一绑定
//2)、这个对象默认也会放在请求域中;取值用的key?
// 1)、对象类名首字母小写; Admin admin
// 2)、不符合驼峰命名的,就是类名;TAdmin
return "forward:/WEB-INF/views/login.jsp";
}
session.setAttribute(AppConst.LOGIN_USER, loginUser);
//2、登录完成跳转到main页面。重定向到main
return "redirect:/main ";
}
1.8.2.2 表单数据回显,并提示错误消息
<div class="alert alert-danger">
${msg }
</div>
<div class="form-group has-success has-feedback">
<input type="text" class="form-control" name="loginacct"
placeholder="请输入登录账号" autofocus value="${TAdmin.loginacct }">
<span class="glyphicon glyphicon-user form-control-feedback"></span>
</div>
1.9 菜单查询并组装完成
1.9.1 TMenu
package com.atguigu.atcrowdfunding.bean;
import java.util.ArrayList;
import java.util.List;
public class TMenu {
private Integer id;
private Integer pid;
private String name;
private String url;
private String icon;
//保存当前菜单的所有子菜单的
private List<TMenu> childMenus = new ArrayList<>();
}
1.9.2 SystemMenuService/SystemMenuServiceImpl
package com.atguigu.atcrowdfunding.service;
import java.util.List;
import com.atguigu.atcrowdfunding.bean.TMenu;
/**系统菜单服务*/
public interface SystemMenuService {
List<TMenu> listMenus();
}
package com.atguigu.atcrowdfunding.service.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.atguigu.atcrowdfunding.bean.TMenu;
import com.atguigu.atcrowdfunding.mapper.TMenuMapper;
import com.atguigu.atcrowdfunding.service.SystemMenuService;
@Service
public class SystemMenuServiceImpl implements SystemMenuService {
@Autowired
TMenuMapper menuMapper;
@Override
public List<TMenu> listMenus() {
// 1、查出所有菜单;
List<TMenu> list = menuMapper.selectByExample(null);
//父菜单保存数组
List<TMenu> parentMenus = new ArrayList<>();
//父菜单按照id缓存起来了
Map<Integer, TMenu> cache = new HashMap<>();
// 2、组装成页面能用的有父子关系的菜单结构
// 2.1)、挑出所有的菜单中哪些是父菜单
for (TMenu parentMenu : list) {
if (parentMenu.getPid() == 0) {
//说明这是一个父菜单
parentMenus.add(parentMenu);
//缓存起来方便以后按照id查询
cache.put(parentMenu.getId(), parentMenu);
}
}
// 2.2)、找到所有父菜单的所有子菜单 8*8=64
for (TMenu tMenu : list) {
//遍历所有子菜单
if(tMenu.getPid()!=0){
//子菜单找到他的父菜单
Integer pid = tMenu.getPid();
//在缓存中找到之前保存的父菜单。按照当前菜单的pid等于缓存中父菜单的id;
TMenu parent = cache.get(pid);
parent.getChildMenus().add(tMenu);
}
}
return parentMenus;
}
}
1.9.3 显示菜单
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<ul style="padding-left:0px;" class="list-group">
<c:forEach items="${loginMenuList }" var="parentMenu">
<c:if test="${empty parentMenu.children}">
<li class="list-group-item tree-closed" >
<a href="${PATH}/${parentMenu.url}"><i class="${parentMenu.icon}"></i>
${parentMenu.name }</a>
</li>
</c:if>
<c:if test="${not empty parentMenu.children}">
<li class="list-group-item tree-closed">
<span><i class="${parentMenu.icon }"></i> ${parentMenu.name}
<span class="badge" style="float:right">${parentMenu.children.size() }</span></span>
<ul style="margin-top:10px;display:none;">
<c:forEach items="${parentMenu.children }" var="menu">
<li style="height:30px;">
<a href="${PATH}/${menu.url}">
<i class="${menu.icon }"></i> ${menu.name }</a>
</li>
</c:forEach>
</ul>
</li>
</c:if>
</c:forEach>
</ul>
转载:https://blog.csdn.net/weixin_43902449/article/details/104723566