🧸安清h:个人主页
🎥个人专栏:【Spring篇】【计算机网络】【Mybatis篇】
🚦作者简介:一个有趣爱睡觉的intp,期待和更多人分享自己所学知识的真诚大学生。
目录
🎯1.登录-持久层
🚦1.1规划需要执行的SQL语句
🚦1.2接口设计和抽象方法
🎯2.登录-业务层
🚦2.1规划异常
✨1.密码匹配失败异常
✨2.用户名没有被找到
✨3.异常的编写
🚦2.2设计业务层接口和抽象方法
🚦2.3抽象方法实现
🎯3.登录-控制层
🚦3.1处理异常
🚦3.2设计请求
🚦3.3处理请求
🎯4.登录-前端页面
🎯用户会话Session
🎯拦截器
当用户输入用户名和密码将数据提交给后台数据库进行查询,如果存在对应的用户名和密码则表示登录成功,登录成功之后跳转到系统的主页,就是index.html页面,跳转在前端使用jquery来完成。
🎯1.登录-持久层
🚦1.1规划需要执行的SQL语句
依据用户提交的用户名和密码做select查询。密码的比较在业务层执行。
select * from t_user where username=?
注意:如果在分析过程中发现某个功能模块已经被开发完成,所以就可以省略当前的开发步骤,这个分析过程不能够省略。 这个功能模块在用户注册部分已经实现过,所以在此无需重复进行了。
🚦1.2接口设计和抽象方法
不用重复开发。单元测试也无需单独执行了。
🎯2.登录-业务层
🚦2.1规划异常
✨1.密码匹配失败异常
用户名对应的密码错误:PasswordNotMatchExcepption异常,运行时异常,业务层异常。
//密码验证失败的异常
public class PasswordNotMatchException extends ServiceException{
public PasswordNotMatchException() {
super();
}
public PasswordNotMatchException(String message) {
super(message);
}
public PasswordNotMatchException(String message, Throwable cause) {
super(message, cause);
}
public PasswordNotMatchException(Throwable cause) {
super(cause);
}
protected PasswordNotMatchException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
✨2.用户名没有被找到
抛出异常:UsernameNotFoundException异常,运行时异常,业务层异常。
//用户数据不存在的异常
public class UserNotFoundException extends ServiceException{
public UserNotFoundException() {
super();
}
public UserNotFoundException(String message) {
super(message);
}
public UserNotFoundException(String message, Throwable cause) {
super(message, cause);
}
public UserNotFoundException(Throwable cause) {
super(cause);
}
protected UserNotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
✨3.异常的编写
- 业务层异常需要继承ServiceException异常类。
- 在具体的异常类中定义构造方法(可以使用快捷键来生成,有5个构造方法)。
🚦2.2设计业务层接口和抽象方法
1.直接在IUserService接口中编写抽象方法,login(String username,String password)。将当前登录成功的用户数据以当前用户对象的形式进行返回。状态管理:可以将数据保存在cookie或者session中,可以避免重复度很高的数据多次频繁操作数据进行获取(用户名、用户id-存放在session中,用户的头像保存在cookie中,而cookie存储在了客户端上)。
//用户模块业务层接口
public interface IUserService {
void reg(User user);
// 用户登录功能
// @param username 用户名
// @param password 用户的密码
// @return 当前匹配的用户数据,如果没有则返回null值
User login(String username,String password);
}
🚦2.3抽象方法实现
1.需要在实现类中实现父接口的抽象方法。
@Override
public User login(String username, String password) {
//根据用户名称查询用户的数据是否存在,如果不在则抛出异常
User result=userMapper.findByUsername(username);
if(result == null){
throw new UserNotFoundException("用户数据不存在");
}
// 检测用户密码是否匹配
// 1.先获取到数据库中的加密之后的密码
String oldPassword = result.getPassword();
// 2.和用户传递过来的密码进行比较
// 2.1先获取盐值:上一次注册时自动生成的盐值
String salt=result.getSalt();
// 2.2将用户的密码按照相同的md5算法的规则进行加密
String newMd5Password = getMD5Password(password,salt);
// 3.将密码进行比较
if(!newMd5Password.equals(oldPassword)){
throw new PasswordNotMatchException("用户密码错误");
}
// 判断is_delete字段值是否为1,为1表示被标记删除
if(result.getIsDelete()==1){
throw new UserNotFoundException("用户数据不存在");
}
// 调用mapper层的findByUsername来查询用户的数据,提升了系统的性能
User user = new User();
user.setUid(result.getUid());
user.setUsername(result.getUsername());
user.setAvatar(result.getAvatar());
// 将当前的用户数据返回,返回的数据是为了辅助其他页面做数据展示使用的(uid,username,avatar)
return user;
}
2.在测试类中测试业务层登录的方法是否可以执行通过。 在test-service-UserServiceTests中编写如下代码:
@Test
public void login(){
User user = userService.login("test01","123");
System.out.println(user);
}
3.如果一个类没有手动创建,直接将这个类复制到项目,idea找不到这个类。之前的缓存导致不能够正常的找到这类的符号。重新构建项目即可。Build->rebuild。
🎯3.登录-控制层
🚦3.1处理异常
业务层抛出的异常是什么,需要在统一的异常处理类中进行统一的捕获和处理,如果也曾抛出的异常已经在统一异常处理类中曾经处理过,则不需要重复添加。在BaseController类中添加如下代码:
else if(e instanceof UserNotFoundException){
result.setState(5001);
result.setMessage("用户数据不存在的异常");
}else if(e instanceof PasswordNotMatchException){
result.setState(5002);
result.setMessage("用户密码错误的异常");
}
🚦3.2设计请求
请求路径:/users/login
请求方式:POST
请求数据:String username,String password,HttpSession session
响应结果:JsonResult<User>
🚦3.3处理请求
在UserController类中编写处理请求的方法。
// 约定大于配置:开发思想来完成,省略大量的配置甚至注解编写
// 1.接收数据方式:请求处理方法的参数列表设置为pojo类型来接受前端的数据,
// SpringBoot会将前端的url地址中的参数名和pojo类的属性名进行比较,
// 如果这两个名称相同,则将值注入到pojo类中对应的属性上
// 2.接收数据方式:请求处理方法的参数列表设置为非pojo类型(本例中为String类型),
// SpringBoot会直接将请求的参数名和方法的参数名直接进行比较,
// 如果名称相同则自动完成值的依赖注入
@RequestMapping("login")
public JsonResult<User> login(String username,String password){
User data = userService.login(username,password);
return new JsonResult<User>(OK,data);
}
}
🎯4.登录-前端页面
1.在login.html页面中依据前面所设置的请求来发送ajax请求。
<script>
$("#btn-login").click(function (){
$.ajax({
url:"/users/login",
type:"POST",
data:$("#form-login").serialize(),
dataType:"JSON",
success:function (json){
if(json.state==200){
alert("登录成功");
// 跳转到系统主页
// 相对路径来确定跳转的页面
location.href="index.html";
}else{
alert("登录失败");
}
},
error:function (xhr){
alert("登录时产生未知的异常"+xhr.message);
}
});
});
</script>
2.如果跳转不了,rebuild项目。
🎯用户会话Session
session对象主要存在服务器端,可以用于保存服务器的临时数据的对象,所保存的数据可以在整个项目中都可以通过访问来获取,把session中的数据看做一个共享的数据。首次登录的时候所获取到的用户数据,转移到session对象即可。session.getAttrbute("key")可以将获取session中的数据这种行为进行封装,封装在BaseController类中。
1.封装session对象中数据的获取(封装在父类中),数据的设置(当用户登录成功后进行数据的设置,设置到全局的session对象中)。
2.在父类中封装两个数据:获取uid和获取username对应的两个方法。用户头像暂时不考虑,将来封装在cookie中来使用。
// 方法不需要被修改,用final来修饰
// 获取session对象中的uid
// @param session session对象
// @return 当前登录的用户uid的值
protected final Integer getuidFromSession(HttpSession session){
return Integer.valueOf(session.getAttribute("uid").toString());
}
// 获取当前登录用户的username
// @param session session对象
// @return 当前登录用户的用户名
protected final String getUsernameFromSession(HttpSession session){
// getAttribute返回的类型是object,所以加上toString转换成字符串
return session.getAttribute("username").toString();
}
3.在登录的方法中将数据封装在session对象中。服务本身自动创建有session对象,已经是一个全局的session对象。SpringBoot直接使用session对象,直接将HttpSession类型的对象做为请求处理方法的参数,会自动将全局的session对象注入到请求处理方法的session形参上。
@RequestMapping("login")
public JsonResult<User> login(String username,String password,HttpSession session){
User data = userService.login(username,password);
// 向session对象中完成数据的绑定(session全局的)
session.setAttribute("uid",data.getUid());
session.setAttribute("username",data.getUsername());
// 获取session中绑定的数据
System.out.println(getuidFromSession(session));
System.out.println(getUsernameFromSession(session));
return new JsonResult<User>(OK,data);
}
🎯拦截器
首先将所有的请求统一拦截到拦截器中,可以在拦截器中来定义过滤的规则,如果不满足系统的设置的过滤规则,统一的处理是重新去打开login.html页面(重定向和转发),推荐使用重定向。
在SpringBoot项目中拦截器的定义和使用。SpringBoot是依靠SPringMVC来完成的。SpringMVC提供了一个HandlerInterceptor接口,用于表示定义一个拦截器。受限制自定义个类,在这个类实现这个接口。
1.首先自定义一个类,在这个类实现HandlerInterceptor接口。
2.注册过滤器:添加白名单(哪些资源可以在不登录的情况下访问:login.html/register.html/login/reg/index.html/product.html),添加黑名单(在用户登录状态下才可以访问的页面资源)。
3.注册过滤器的技术:借助WebMvcConfigure接口,可以将用户定义的拦截器进行注册,注册后才可以保证拦截器能够生效和使用。定义一个类,然后让这个类实现WebMvcConfigure接口。配置信息,建议存放在项目的config包结构下。
//处理器拦截器的注册
@Configuration //加载当前的拦截器并进行注册
public class LoginInterceptorConfigurer implements WebMvcConfigurer {
// 配置拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 创建自定义拦截器对象
HandlerInterceptor interceptor = new LoginInterceptor();
// 配置白名单;存放在List集合中
List<String> patterns = new ArrayList<>();
patterns.add("/bootstrap3/**");
patterns.add("/css/**");
patterns.add("/images/**");
patterns.add("/js/**");
patterns.add("/web/register.html");
patterns.add("/web/login.html");
patterns.add("/web/index.html");
patterns.add("/web/product.html");
patterns.add("/users/reg");
patterns.add("/users/login");
// 拦截器的注册
registry.addInterceptor(interceptor)
.addPathPatterns("/**") //表示要拦截的url是什么
.excludePathPatterns(patterns); //除了哪些路径之外
}
}
4.如果短时间内多次访问,会提示重定向次数过多,login.html页面无法打开。将浏览器cookie请求,再将浏览器设置为初始设置。
解释:
//将自定义拦截器进行注册
default void addInterceptors(InterceptorRegistry registry) {
}
源码解析:
public interface HandlerInterceptor {
//在调用所有处理请求的方法之前被自动调用执行的方法
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
//在ModelAndView对象返回之后被调用的方法
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
//在整个请求所有关联的资源被执行完毕最后所执行的方法
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}