用户登录
当用户输入用户名和密码将数据提交给后台数据库进行查询,如果存在对应的用户名和密码则表示登录成功,登录成功之后跳转到系统的主页就是index.html页面,跳转在前端使用jquery来完成。
(一)登录(持久层)
1.规划sql语句
依据用户提交的用户名和密码做select查询。
select * from t_user where username=?
2.设计接口和抽象方法
在UserMapper接口的findByUsername方法已经实现了这个方法
(二)登录(业务层)
1.异常规划
用户不存在UsernameNotFoundException异常(运行时异常)
public class UsernameNotFoundException extends ServiceException { /**重写ServiceException的所有构造方法*/ }
密码错误PasswordNotMatchException异常(运行时异常)
public class PasswordNotMatchException extends ServiceException{ /**重写ServiceException的所有构造方法*/ }
2.设计接口和抽象方法及实现
在IUserService接口中编写抽象方法login(String username,String password)login(User user)也是可以的
//登录 User login(String username,String password);
在UserServiceimpl中实现该方法
@Override public User login(String username, String password) { //根据用户名称来查询用户的数据是否存在,不存在则抛出异常 User result = userMapper.findByUsername(username); if (result == null) { throw new UsernameNotFoundException("用户数据不存在"); } /** * 检测用户的密码是否匹配: * 1.先获取数据库中加密之后的密码 * 2.和用户传递过来的密码进行比较 * 2.1先获取盐值 * 2.2将获取的用户密码按照相同的md5算法加密 */ String oldPassword = result.getPassword(); String salt = result.getSalt(); String newMd5Password = getMD5Password(password, salt); if (!newMd5Password.equals(oldPassword)) { throw new PasswordNotMatchException("用户密码错误"); } //判断is_delete字段的值是否为1,为1表示被标记为删除 if (result.getIsDelete() == 1) { throw new UsernameNotFoundException("用户数据不存在"); } //方法login返回的用户数据是为了辅助其他页面做数据展示使用(只会用到uid,username,avatar) //所以可以new一个新的user只赋这三个变量的值,这样使层与层之间传输时数据体量变小,后台层与 // 层之间传输时数据量越小性能越高,前端也是的,数据量小了前端响应速度就变快了 User user = new User(); user.setUid(result.getUid()); user.setUsername(result.getUsername()); user.setAvatar(result.getAvatar()); return user; }
(三)登录(控制层)
1.处理异常
业务层抛出的异常需要在统一异常处理类中进行统一的捕获和处理,如果该异常类型已经在统一异常类中曾经处理过则不需要重复添加
}else if (e instanceof UsernameNotFoundException){ result.setState(5001); result.setMessage("用户不存在"); }else if (e instanceof PasswordNotMatchException){ result.setState(5002); result.setMessage("密码错误"); }
2.请求设计
请求路径:/users/login
请求参数:String username,String password
请求类型:POST
响应结果:JsonResult< User>
3.请求处理
在UserController类中编写处理请求的方法.编写完成后启动主服务验证一下
@PostMapping("/login/{username}/{password}") public JsonResult<User> login(@PathVariable String username,@PathVariable String password){ User user=userService.login(username,password); return new JsonResult<User>(ok,user); }
注意,控制层方法的参数是用来接收前端数据的,接收数据方式有两种:
请求处理方法的参数列表设置为非pojo类型:
SpringBoot会直接将请求的参数名和方法的参数名直接进行比较,如果名称相同则自动完成值的依赖注入
请求处理方法的参数列表设置为pojo类型:
SpringBoot会将前端的url地址中的参数名和pojo类的属性名进行比较,如果这两个名称相同,则将值注入到pojo类中对应的属性上
这两种方法都没有使用注解等等花里胡哨的,却能正常使用,原因是springboot是约定大于配置的,省略了大量配置以及注解的编写
(四)登录(前端)
1.前端页面代码
在login.html中编写前端代码
<script type="text/javascript"> //1.监听按钮是否被点击 $("#btn-login").click(function () { $.ajax({ url:"/users/login", type:"POST", data:$("#form-login").serialize(), dataType:"JSON", //2.发送ajax()的异步请求来完成用户的注册功能 success:function (json) { if (json.state==200){ alert("登录成功"); //跳转系统主页index。html //相对路径 location.href="index.html"; }else{ alert("登录失败"); } }, error:function (xhr) { alert("登录产生未知异常"+xhr.message); } }); }); </script>
2.用户会话session
session对象主要存在服务器端,可以用于保存服务器的临时数据的对象,所保存的数据可以在整个项目中都可以通过访问来获取,把session的数据看做一个共享的数据。首次登录的时候所获取的用户的数据,转移到session对象即可。seession.getAttrbute(“key”)可以将获取session中的数据这种行为进行封装,封装在BaseController类中。
1.封装session对象中数据的获取(封装父类中)、数据的设置(当用户登录成功后进行数据的设置,设置到全局的session对象)。
2.在父类中封装两个数据:获取uid和获取username对应的两个方法。用户头像暂时不考虑,将来封装cookie中来使用。
//获取当前登录的id protected final Integer getuidfromsession(HttpSession session){ return Integer.valueOf(session.getAttribute("uid").toString()); } //获取当前登录用户名 protected final String getusernamesession(HttpSession session){ return session.getAttribute("username").toString(); }
3.在登录的方法中将数据封装在session对象中。服务本身自动创建有session对象,已经是一个全局的session对象。SpringBoot直接使用session对象,直接将HttpSession类型的的对象做为请求处理方法的参数,会自动将全局的session对象注入到请求处理方法的session形参上。
@PostMapping("/login") public JsonResult<User> login(String username, String password, HttpSession session){ User user=userService.login(username,password); session.setAttribute("uid",user.getUid()); session.setAttribute("username",user.getUsername()); return new JsonResult<User>(ok,user); }
(五)拦截器
拦截器:首先将所有的请求统一拦截到拦截器中,可以拦截器中来定义过滤的规则,如果不满足系统的设置的过滤规则,统一的处理是重新去打开login.html页面(重定向和转发),推荐使用重定向(重定向和转发都是资源的跳转,前者是在浏览器端跳转,会产生两次请求,地址栏url会变化,后者转发是在服务器端跳转,且只会产生一次请求,地址栏url不会变化)
在SpringBoot项目中拦截器的定义和使用。SpringBoot是依靠springMVC来完成的。SpringMVC提供了一个HandlerInterceptor接口,用于表示定义一个拦截器。首先自定义个类,在这个类实现这个接口。
1.首先自定义一个类,在这个类实现这个Handlerlnterceptor接口。
public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 读取session对象 Object obj=request.getSession().getAttribute("uid"); if (obj==null){ //说明用户没有登录过,则重定向到login。html response.sendRedirect("/web/login.html"); //结束后续调用 return false; } //请求放行 return true; } }
源码分析
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 { } }
注册过滤器:添加白名单(哪些资源可以在不登录的情况下访问:
login.htmllregister.htmNlogin(reglindex.htm/product.html)、添加黑名单(在用户登录的状态才可以访问的页面资源)。
注册过滤器的技术:借助WebMvcConfigure接口,可以将用户定义的拦截器进行注册,才可以保证拦截器能够生效和使用。定义一个类,然后让这个类实现WebMvcConfigure接口。配置信息,建议存放在项目的config包结构下。
//处理器拦截器的注册 @Configuration public class LoginInterceptorConfigurer implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { HandlerInterceptor interceptor=new LoginInterceptor(); 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("/**") .excludePathPatterns(patterns); } }
修改密码
需要用户提交原始密码和新密码,再根据当前登录的用户进行信息的修改操作
(一)修改密码(持久层)
1.sql规划
根据用户的uid修改用户password值
update t_user set password=?,modified_user=?, modified_time=? WHERE uid=?
在执行修改密码之前,还应检查用户数据是否存在或者用户数据是否被标记为"已删除"、并检查原密码是否正确,这些检查都可以通过查询用户数据来辅助完成
SELECT * FROM t_user WHERE uid=?
2.设计接口和抽象方法
mapper接口
//根据uid修改密码 Integer updatePasswordByUid(Integer uid, String password, String modifiedUser, Date modifiedTime); //根据uid查询 User findUid(Integer uid);
mapper.xml映射文件
<update id="updatePasswordByUid"> update t_user set password=#{password}, modified_user=#{modifiedUser}, modified_time=#{modifiedTime} where uid=#{uid} </update> <select id="findUid" resultMap="UserEntityMap"> select * from t_user where uid=#{uid} </select>
(二)修改密码(业务层)
1.规划异常
1.用户的源密码错误,is_delete==1、uid找不到,在用户没有发现的异常。2.update在更新的时候,有可能产生未知的异常,UpdateException。
//更新时产生异常 public class UpdateException extends ServiceException{ /**重写ServiceException的所有构造方法*/ }
2.设计接口和抽象方法
执行用户修改密码的核心方法。
//修改密码 void changePassword(Integer uid,String username,String oldPassword,String newPassword);
serviceimpl实现该方法
@Override public void changePassword(Integer uid, String username, String oldPassword, String newPassword) { User user=userMapper.findUid(uid); if (user==null || user.getIsDelete()==1){ throw new UsernameNotFoundException("用户不存在"); } //密码比较 String salt=user.getSalt(); String oldpassword2=getMd5Password(oldPassword,salt); if (!oldpassword2.equals(user.getPassword())){ throw new PasswordNotMatchException("密码不正确"); } //将新密码设置进去 String newpassword2=getMd5Password(newPassword,salt); user.setPassword(newpassword2); Integer rows= userMapper.updatePasswordByUid(uid,newpassword2,user.getUsername(),new Date()); if (rows!=1){ throw new UpdateException("修改时出现未知异常"); } }
三)修改密码(控制层)
1.异常处理
UpdateException需要配置在统一的异常处理方法中。(basecontroller中)
}else if (e instanceof UpdateException){ result.setState(5001); result.setMessage("修改出现异常"); }
2.设计请求
请求路径:/users/change_password
请求类型:post
请求参数:String oldPassword,String newPassword,HttpSession session(uid和username可以通过session获取到,在处理方法的内部获取就可以了)如果参数名用的是非pojo类型,就需要和表单中的name属性值保持一致
响应结果:JsonResult< void>
3.处理请求
@PostMapping("/change_password") public JsonResult<Void> update(String oldPassword,String newPassword,HttpSession session){ Integer getuidfromsession = getuidfromsession(session); String getusernamesession = getusernamesession(session); userService.changePassword(getuidfromsession,getusernamesession,oldPassword,newPassword); return new JsonResult<>(ok); }
(四)修改密码(前端页面)
在password.html中添加ajax请求的处理
<script type="text/javascript"> //1.监听按钮是否被点击 $("#btn-change-password").click(function () { $.ajax({ url:"/users/change_password", type:"POST", data:$("#form-change-password").serialize(), dataType:"JSON", //2.发送ajax()的异步请求来完成用户的注册功能 success:function (json) { if (json.state==200){ alert("密码修改成功"); //跳转系统主页index。html //相对路径 location.href="login.html"; }else{ alert("密码修改失败失败"); } }, error:function (xhr) { alert("修改密码产生未知异常"+xhr.message); } }); }); </script>
后记
👉👉💕💕美好的一天,到此结束,下次继续努力!欲知后续,请看下回分解,写作不易,感谢大家的支持!! 🌹🌹🌹