目录
一、发送邮件
1,邮箱设置
-启用客户端SMTP服务(在对应的邮箱里设置)
2.Spring Email
-导入jar包(在mvnrepository里找),导入pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
-邮箱参数配置:不要把邮箱参数写死 在application.properties
-使用JavaMailSender(创建util包放工具类)
MailClient.java 加注解@Component
主动注入JavaMailSender
3.用模板引擎
-使用Thymeleaf模板发送HTML文件
在templates文件夹中创建模板,放在mail文件中
二、开发注册功能
1.访问注册页面
-点击顶部区域内的链接,打开注册页面(使用thymeleaf标签实现html代码块复用)
1)创建LoginController,在其中写获得注册和登录页面的方法
2)在首页index.html代码中,写点击连接的路径,语法为:
//这里的register就是@RequestMapping里写的那个
<a class="nav-link" th:href="@{/register}">注册</a>
3)在html文件中,在头部header标签里加fragment标签并取名字就可以实现在其他HTML里复用这个代码块,复用的方法是在其他html文件中这么写:replace里写哪个html取得什么名字
<header class="bg-dark sticky-top" th:replace="index::header">
2.提交注册数据
-通过表单提交数据
1)加载依赖 commons lang
2)在application.properties中新建配置
community.path.domain=http://localhost:8080
3)在unity中新建工具类:CommunityUtil.java 写一写封装好的静态自定义方法
写实现生成随机字符串方法
4)在UserService里写注册的相关方法
-服务端验证账号是否已经存在、邮箱是否已经注册
UserService里判断账号是否已经在数据库里
-服务端发送激活邮件
UserService里写发邮件代码
LoginController里写实现register方法
在register.html中作一系列设置,主要包括各种异常情况的提示
3.激活注册账号
-点击邮件中的链接,访问服务端的激活服务
1)在service层加一个业务方法:激活成功 or 重复激活 or 激活失败
2)在unity文件夹下定义一个新的常量接口 CommunityConstant.java
3)在UserService中实现这个常量接口 activation
4)在LoginController中新增激活的方法activation,在方法中调用UserService中的activation方法,也要实现常量接口activation来识别不同的状态码
5)在LoginController中新增激活成功后跳转至登录界面
三、会话管理
1.会话管理相关
HTTP的基本性质:简单;可扩展;无状态(同一个浏览器向服务器发送多次请求,请求之间是独立、没有关系的,浏览商品,商品加购物车,这之间是没有关联的,服务器不能识别你是谁)、有会话
Cookie:将它加到头部,创建一个会话让每次请求都共享相同的上下文信息。
-服务器用于识别浏览器的。服务器发送到用户浏览器并保存在本地的一小块数据。
-他会在浏览器下次向同一服务器发送请求是被携带并发送到服务器,用于告知服务器两个请求是否来自同一个浏览器。
缺点是存在客户端,会有安全问题,容易被盗,因此不能存隐私的数据。在很多请求中,cookie每次都会发给服务器,会影响性能。
在AlphaController里做cookie实例
Session:它是依赖于cookie的,每个浏览器访问服务器都会在服务器创建一个session,浏览器和session的对应是依靠cookie的
JavaEE的标准,用于在服务端记录客户端信息。
数据存放在服务端更加安全,但是会增加服务端内存压力。
在AlphaController里做session实例
分布式部署情况下用得少,会有问题。
问题:每次访问可能不是在一个服务器,这个服务器创建的session,另一个服务器里没有
解决: 粘性session:每次同一个ID都分给同一个服务器
同步session:服务器创建session时会同步给其他session。会导致服务器之间出现耦合。
共享session:专门用一台服务器专门处理session。这个服务器挂了就GG了 。
主流办法:客户端数据尽可能存cookie,不方便存的放数据库。可以存nosql数据库里:存Redis里
四、生成验证码
1.Kaptcha
-导入jar包
-编写Kaptcha配置类
kaptcha.config
-生成随机字符、生成图片
-LoginController写一个请求向浏览器返回图片,方法名getKaptcha
-在login。html里把对应的路径修改了
-实现点击刷新验证码的按钮时刷新验证码,修改按钮对应的js方法,新建一个js方法。在global.js里可以声明一个常量,实现在引用路径的时候可以不把路径写死,方便修改
五、登录、退出功能
1.访问登录页面
-点击顶部区域内的链接跳转登录界面
2.登录(逻辑依旧是 数据访问层 ,业务层,表现层)
-验证账号、密码、验证码。
-成功是,生成登录凭证(存在数据库login_ticket里 ),发放给客户端。
-失败时,跳转回登录页。
3.退出
-将登录凭证修改为失效状态。
-跳转至网站首页。
①entity里写LoginTicket实体类,声明的属性包含数据库中所有的字段。生成get set方法,toString()方法
②dao包下写数据访问的逻辑,新建LoginTicketMapper接口,文件前面声明注解@Mapper表明这是一个数据访问对象,需要容器管理。
在Mapper接口中写增删改查的一些数据访问方法。
在这里Ticket是查询的关键,因此创建 int insertLoginTicket,LoginTicket selectByTicket,int updateTicket。
实现这个接口有两种方法:1)在resources/mapper文件夹下创建xml配置文件,在每个配置文件里写实现上面方法对应的sql语句。2)直接在这个Mapper类中写注解,通过注解的方法声明SQL语句。这次功能用第二种方法实现。
③userService里写逻辑方法
④在LoginController里写账户密码验证码验证的的方法,实现相应跳转
⑤修改HTML文件中对应变量 name字段是传给controller 的
功能实现流程总结:
entity:最近接近数据库,创建实体类里面的属性全部都是数据库中的字段,生成get set方法,toString方法。
dao:写Mapper接口,里面写要对数据库中表想要进行的一些操作的方法。(要写一个对应的xml配置文件写每个方法对应的sql语句)
service:写对应的service文件,里面是一系列的逻辑操作,返回值喜欢用map(对于出现的错误生成返回值),包括判断前端的输入输出是否合法,然后在service文件里调用Mapper接口中的一些方法完成数据的插入。
controller(表现层逻辑):在controller层中则是调用service层中的方法进行一系列的页面跳转或显示的操作,将一些参数传递给service层的方法。
六、显示登录信息
在每个页面头部要显示用户头像以及点按钮显示个人信息等。并且登录与否会影响网页的显示的内容
1.拦截器示例:拦截浏览器访问过来的请求,拦截请求后可以在请求的开始或结束部分插入代码解决一些共有的需求。能以低耦合度方式解决系统请求,主要用于拦截前端请求,常用于登录检查。
新建一个interceptor包(写在controller/interceptor/LoginTicketInterceptor)是否登录显示不一样的界面
-定义拦截器,实现HandlerInterceptor
三个方法:
prehandle:在controller之前执行
postHandle:在controller之后执行
afterCompletion:在模板引擎执行后执行
-配置拦截器,为他制定拦截、排除的路径。
config文件夹下新建配置类WebMvcConfig.java 配置类完成拦截器配置
2.拦截器应用
-在请求开始时查询登录用户
①新建LoginTicketInterceptor重写preHandle (ps:新建cookieUtil工具类,用于从cookie中取值)
②在UserService中新建LoginTicket findLoginTicket(String ticket)方法。
③ 写查询用户的方法
-在本次请求中持有用户数据(存用户要考虑多线程并发,考虑线程隔离)
①用ThreadLocal隔离存用户数据:Util包下新建HostHolder,起容器作用,持有用户信息,用于代替session对象。
ThreadLocal对象创建,存值,取值,清理。
(实现线程隔离方法:先获取当前线程,再取线程对应的key。)
②将用户数据存入hostHolder
-在模板视图上显示用户数据
在模板引擎用用户数据之前就要把用户数据存入model中,这样模板引擎才能用。
①重写postHandle(在模板引擎执行前运行)
从hostHolder中取用户数据。
用户和modeAndView不为空时,把用户加入modeAndView中
-在请求结束时清理用户数据
重写afterCompletion,调用hostHolder里的clear方法。
在浏览器端存ticket代替直接存用户数据,避免出现用户数据隐私泄露问题,改用存ticket,根据ticket查数据库login_ticket表进而获得user数据,将其放入模板后生成HTML文件传给浏览器
用ThreadLocal完成多线程间的隔离,新建工具类HostHolder,实现容器的作用。里面创建ThreadLocal,在存取过程中实现线程隔离。
方法:每个线程有不同的map对象 :获得当前线程,根据当前线程创建一个map对象实现set get方法
七、账号设置
用户上传头像的功能
SpringMVC通过MutipartFile上传文件
八、检查登录状态
防止出现用户在未登录情况下可以直接通过url访问不能访问的功能
1.使用拦截器
-在方法前标注自定义注解
-拦截所有请求,只处理带有该注解的方法。
2.自定义注解(对应上面说的在方法前标注的那个自定义注解)
要用元注解定义自定义注解,元注解用来声明自定义注解的可以写在什么地方,生存时间、是否写入文档、是否能继承,
用反射方法读取注解
①annotation包下新建LoginRequired.java定义注解 @LoginRequire,声明注解的写的位置,作用在什么类型上;生存时间。
②在需要的方法前面加上自定义注解 @LoginRequire
③ 写拦截器新建LoginRequiredInterceptor.java 写拦截后处理的方法LoginRequiredInterceptor
// 判断拦截到的是不是方法,因为有可能是静态资源
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
// 尝试从method方法中取到注解
LoginRequired loginRequired = method.getAnnotation(LoginRequired.class);
if (loginRequired != null && hostHolder.getUser() == null) {
// 用户未登录则重定向到登录页
response.sendRedirect(request.getContextPath() + "/login");
return false;
}
}