基于springboot框架的电脑商城项目(二)
基于springboot框架的电脑商城项目(二)
- 用户登录
- (一)登录(持久层)
- 1.规划sql语句
- 2.设计接口和抽象方法
- (二)登录(业务层)
- 1.异常规划
- 2.设计接口和抽象方法及实现
- (三)登录(控制层)
- 1.处理异常
- 2.请求设计
- 3.请求处理
- (四)登录(前端)
- 1.前端页面代码
- 2.用户会话session
- (五)拦截器
- 修改密码
- (一)修改密码(持久层)
- 1.sql规划
- 2.设计接口和抽象方法
- (二)修改密码(业务层)
- 1.规划异常
- 2.设计接口和抽象方法
- (三)修改密码(控制层)
- 1.异常处理
- 2.设计请求
- 3.处理请求
- (四)修改密码(前端页面)
- 后记
用户登录
当用户输入用户名和密码将数据提交给后台数据库进行查询,如果存在对应的用户名和密码则表示登录成功,登录成功之后跳转到系统的主页就是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>
后记
👉👉💕💕美好的一天,到此结束,下次继续努力!欲知后续,请看下回分解,写作不易,感谢大家的支持!! 🌹🌹🌹