Java十天上手做一个自己的web商城笔记-用户登录(4)

    该系列博客主要记录笔者的开发过程,参考B站系列视频:【SpringBoot项目实战完整版】SpringBoot+MyBatis+MySQL电脑商城项目实战_哔哩哔哩_bilibili

所用的一些版本信息:

IDEA开发、JDK1.8版本以上、maven3.61版本以上,springboot,DataGrip管理数据库

用户登录大致页面

主要功能:

    查询用户的用户名和密码,存在用户密码正确则表明登录成功,之后跳转系统的index.html页面

1.登录-持久层

1.1 规划sql语句

依据用户提交用户名进行select查询

在用户登录时已经编写了findByUserName函数,所以无需重复编写。

1.2 接口设计和抽象方法

 同样,这一步无需重复开发

1.3 单元测试

 同样,这一步无需重复开发

2.登录-业务层

2.1 规划异常

1.密码匹配失败异常,PasswordNotMatchException异常。

package com.cy.store.service.ex;

/** 密码不匹配异常 */
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异常。

package com.cy.store.service.ex;

/** 用户名未找到异常 */
public class UsernameNotFoundException extends ServiceException{
    public UsernameNotFoundException() {
    }

    public UsernameNotFoundException(String message) {
        super(message);
    }

    public UsernameNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }

    public UsernameNotFoundException(Throwable cause) {
        super(cause);
    }

    public UsernameNotFoundException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

2.2 设计业务层接口和抽象方法

1.在IUserService中编写抽象方法login,并返回用户数据,将数据保存在cookie或session中,可以避免重复度很高的数据多次频繁操作数据进行获取。

在store\src\main\java\com\cy\store\service\IUserService.java中写抽象方法:

    /**
     * 用户登录方法
     * @param username
     * @param password
     * @return 当前匹配的用户数据,没有则返回null
     */
    User login(String username, String password);

2.3 抽象方法的实现

在store\src\main\java\com\cy\store\service\impl\UserServiceImpl.java中对抽象方法进行实现:

    @Override
    public User login(String username, String password) {
        //根据用户名称查询用户数据是否存在,不存在则抛异常
        User result = userMapper.findByUserName(username);
        if(result == null){
            throw new UsernameNotFoundException("用户数据不存在");
        }
        //检测用户密码是否匹配
        String newMD5password = getMD5Password(password, result.getSalt());
        if(!newMD5password.equals(result.getPassword())){
            throw new PasswordNotMatchException("用户密码错误");
        }
        //判断是否逻辑删除(is_delete)
        if(result.getIsDelete()==1){
            throw new UsernameNotFoundException("用户数据不存在");
        }

        //返回的user是为了辅助页面数据展示使用,只需要部分字段,提升前后端数据传递的性能
        User user = new User();
        user.setUid(result.getUid());
        user.setUsername(result.getUsername());
        user.setAvatar(result.getAvatar());
        return result;
    }

2.4 单元测试

在 store\src\test\java\com\cy\store\service\UserServiceTests.java 中进行单元测试

    @Test
    public void login(){
        User user = userService.login("liyujie", "123");
        System.out.println(user);
    }

成功!

3.登录-控制层

3.1 处理异常

业务层抛出的异常是什么,在统一异常类BaseController进行捕获处理。

在store\src\main\java\com\cy\store\controller\BaseController.java 中添加异常处理

package com.cy.store.controller;

/** 控制层类的基类 */
import com.cy.store.service.ex.*;
import com.cy.store.util.JsonResult;
import org.springframework.web.bind.annotation.ExceptionHandler;

/** 主要是处理异常 */
public class BaseController {
    /** 操作成功的状态码 */
    public static final int OK = 200;

    // 请求处理方法,这个方法的返回值,需要传统给前端数据
    @ExceptionHandler(ServiceException.class) //该注解用于统一处理抛出的异常
    public JsonResult<Void> handleException(Throwable e){
        JsonResult<Void> result = new JsonResult<>(e);
        if(e instanceof UsernameDuplicatedException){
            result.setState(4000);
            result.setMessage("用户名占用");
        }else if(e instanceof UsernameNotFoundException){
            result.setState(4001);
            result.setMessage("用户数据不存在异常");
        }else if(e instanceof PasswordNotMatchException){
            result.setState(4002);
            result.setMessage("用户的密码错误的异常");
        }else if(e instanceof InsertException){
            result.setState(5000);
            result.setMessage("注册时产生未知异常");
        }
        return result;
    }
}

3.2 设计请求

请求路径:/users/login

请求方式:POST

请求数据:String username, String password,HttpSession

响应结果:JsonResult<User>

3.3 处理请求

在store\src\main\java\com\cy\store\controller\UserController.java 编写处理请求方法

    @RequestMapping("login")
    public JsonResult<User> login(String username, String password){
        User data = userService.login(username, password);
        return new JsonResult<User>(OK,data);
    }

4.登录-前端页面

在 store\src\main\resources\static\web\login.html 中添加如下代码:

	    <script type="text/javascript">
			$("#btn-login").click(function(){
				$.ajax({
					url:"/users/login",
					type: "POST",
					data: $('#form-login').serialize(),
					dataType:"JSON",
					success:function (json){
						console.log(json)
						if(json.state == 200){
							alert("登录成功");
							//跳转系统主页 相对路径写法确定跳转页面
							location.href = "index.html";
						}else{
							alert("登录失败" + json.message);
						}
					},
					error: function(xhr){
						alert("登录时产生未知异常" + xhr.message)
					}
				});
			});
		</script>

5.登录-session存储用户数据

用户会话session,主要存储在服务器中,用于保存服务器的临时数据的对象,所保存的数据可以在整个项目通过访问获取。首次登录时将获取的用户数据转移到session对象中。

在该项目里将session封装在BaseController中。

在store\src\main\java\com\cy\store\service\impl\UserServiceImpl.java 中编写获取session对象的uid或username的方法

    /**
     * 获取session对象中的uid
     * @param session
     * @return 当前登录的用户的uid值
     */
    protected final Integer getuidFromSession(HttpSession session){
        return Integer.valueOf(session.getAttribute("uid").toString());
    }

    /**
     * 获取session对象的username
     * @param session
     * @return
     */
    protected final String getusernameFromSession(HttpSession session){
        return session.getAttribute("username").toString();
    }

在登录方法中将数据封装在session对象中,服务器本身自动创建session对象。

springBoot直接使用session对象,直接将httpSession类型的对象作为请求处理的参数(客户端每次提交都会戴上cookie,cookie中有sessionid,而springboot让我们可以直接使用session对象)。

在store\src\main\java\com\cy\store\controller\UserController.java 中的login方法中添加代码如下:

    @RequestMapping("login")
    public JsonResult<User> login(String username, String password, HttpSession session){
        User data = userService.login(username, password);
        //向session对象中完成数据绑定
        session.setAttribute("uid", data.getUid());
        session.setAttribute("username",data.getUsername());

        return new JsonResult<User>(OK,data);
    }

6.登录-拦截器

防止用户在未登录时通过url请求访问到登录后才能访问的网页。

首先将所有的请求统一拦截到拦截器中,可以在拦截器中定义过滤的规则。不满足系统的设置的过滤规则,统一的处理时重新去打开login.html页面(重定向和转发),推荐使用重定向。

在SpringBoot项目中拦截器的定义和使用。SpringBoot是依靠springMVC来完成的。SpringMVC提供了一个HanlerInterceptor接口,用于表示定义一个拦截器。

6.1 编写拦截器

在 store\src\main\java\com\cy\store\interceptor 下编写如下代码:

package com.cy.store.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/** 定义一个拦截器 */
public class LoginInterceptor implements HandlerInterceptor {
    /**
     * 在调用所有处理请求的方法之前自动调用执行方法
     * 检测全局session对象中是否有uid数据,有则放行,没有则重定向到login页面
     * @param request 请求对象
     * @param response 响应对象
     * @param handler 处理器(url+Controller:映射)
     * @return true则放行,false表示拦截
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //HttpServletRequest对象获取session对象
        Object obj = request.getSession().getAttribute("uid");
        if(obj==null){
            //用户没登录,重定向到login
            response.sendRedirect("/web/login.html");
            //结束后续调用
            return false;
        }
        //请求放行
        return true;
    }

    /**
     * 在modelAndView对象返回之后被调用的方法
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    /**
     * 在整个请求关联的所有资源被执行完毕最后所执行的方法
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

6.2 注册过滤器

添加白名单:哪些资源可以在不登录的情况下访问: 登录,注册,主页,商品页面等。

添加黑名单:用户登录状态下才可以访问的资源。

借助webMvcConfigure接口可以将用户定义的拦截器进行注册,才能保证拦截器生效和使用,配置信息简易存放在项目Config包结构下。

package com.cy.store.config;

import com.cy.store.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.ArrayList;
import java.util.List;

/** 处理器拦截器的注册 */
@Configuration
public class LoginInterceptorConfigurer implements WebMvcConfigurer {
    /**
     * 将自定义的拦截器进行注册
     * @param registry
     */
    @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/**");
        //web下放行页面
        patterns.add("/web/register.html");
        patterns.add("/web/login.html");
        patterns.add("/web/index.html");
        patterns.add("/web/product.html");
        //url
        patterns.add("/users/reg");
        patterns.add("/users/login");


        //添加自定义的拦截器对象,完成拦截器注册
        registry.addInterceptor(interceptor)
                .addPathPatterns("/**")        //黑名单,表示拦截的url是什么
                .excludePathPatterns(patterns);//白名单,表示除了什么url
        WebMvcConfigurer.super.addInterceptors(registry);
    }
}

tips:这里切勿反复进行重定向访问,可能会被浏览器认为是非法操作。

测试:

登录成功! 

跳转成功!

输入url:   http://localhost:8081/web/password.html 访问

 重定向成功!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值