互联网技术IT交流圈SSM搭建二手市场交易平台网站

SSM搭建二手市场交易平台(六):用户登录模块

2019-01-20  本文已影响18人  啃饼小白

写在前面

本篇开始,我们介绍用户模块功能,里面包含的知识点很多,具体有:登录;用户名验证;注册;忘记密码;提交问题答案;重置密码;获取用户信息;更新用户信息;退出登录等。

学到的技术

1、理解横向越权、纵向越权安全漏洞;
2、MD5 明文加密机增加salt 值;
3、Guava 缓存的使用;
4、高复用服务响应对象的设计思想及抽象分装;
5、Mybatis-plugins的使用技巧
6、session 的使用;
7、方法局部演进。

横向越权、纵向越权安全漏洞介绍

横向越权:攻击者尝试访问与他拥有相同权限的用户的资源
纵向越权:低级别攻击者尝试访问高级别用户的资源

高复用服务响应对象的设计思想及抽象分装

这里的意思就是说对于一些经常使用到的代码,我们对其进行了封装,以便更好的进行使用。

用户登录功能实现

在我们之前定义的controller包下新建一个包,名称为portal(门户的意思就是给前端用的,你也可以取其他的名字,随意),然后在里面创建一个类,取名UserController。

注意一下,这里我就不再按照之前那种写到哪里,代码就粘贴到哪里的方式了,而是直接附上功能较为完整的代码

在UserController.java里面写入如下代码:

package top.store.controller.portal;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import top.store.common.Const;
import top.store.common.ServerResponse;
import top.store.pojo.User;
import top.store.service.IUserService;

import javax.servlet.http.HttpSession;

/***
 * 用户登录
 * @author lenovo
 * */
@Controller
@RequestMapping("/user/")  //这是我们对整个用户功能配置的一个类似于namespace的东西
public class UserController {

    @Autowired
    private IUserService iUserService;

    @RequestMapping(value = "login.do",method = RequestMethod.POST)  //这里就是具体的每个方法的url链接
    @ResponseBody   //自动序列化json功能
    public ServerResponse<User> login(String username, String password, HttpSession session){
        //service-->mybatis-->dao
        ServerResponse<User> response = iUserService.login(username,password);
        if(response.isSuccess()){
            session.setAttribute(Const.CURRENT_USER,response.getData());
        }
        return response;
    }
}

接着在service这个包里面创建一个IUserService的java文件(为什么取这个名字,是为了一眼知道这个是接口):

IUserService 代码如下:

package top.store.service;

import top.store.common.ServerResponse;
import top.store.pojo.User;

/**@author lenovo
 * */
public interface IUserService {
    ServerResponse<User> login(String username, String password);
}

然后继续在service这个包里面创建一个包impl(顾名思义这个就是实现接口的类的包,同样名字随意)存放它的实现类,接着在该impl文件下新建UserServiceImpl.java文件,让它去实现我们刚才定义的IUserService接口,里面的代码如下:

package top.store.service.impl;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.store.common.ServerResponse;
import top.store.dao.UserMapper;
import top.store.pojo.User;
import top.store.service.IUserService;


@Service("iUserService")
public class UserServiceImpl implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public ServerResponse<User> login(String username, String password) {   //必须要实现接口的方法,否则该类就变成了一个抽象类
        int resultCount = userMapper.checkUsername(username);  //检查登录时的用户名是否存在
        if(resultCount ==0){
            return ServerResponse.createByErrorMessage("用户名不存在");
        }

        //todo  用户名登录MD5
        User user =userMapper.selectLogin(username,password);
        if(user==null){
            return ServerResponse.createByErrorMessage("密码错误");
        }

        user.setPassword(StringUtils.EMPTY);
        return ServerResponse.createBySuccess("登录成功",user);
    }
}

接下来为了完成一个通用的响应对象,我们需要在common这个包里面里新建一个类 ServerResponse(顾名思义就是处理响应的类),里面的代码如下:

package top.store.common;

import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.map.annotate.JsonSerialize;

import java.io.Serializable;

//保证序列化 JSon 的时候,如果是 null 的对象,可以也会消失
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)   //使用这个的作用就是有的时候返回只有状态而没有其他的data和msg也是可以的,就是Key存在但是value为null
public class ServerResponse<T> implements Serializable {   //使用泛型来声明这个类,代表这个类需要封装的数据的类型,并且我们是使用它来进行序列化的

    //这里序列化的对象的确定是根据接口来的
    private int status;
    private String msg;
    private T data;  //使用泛型的最大好处就是,我们可以指定泛型的类型,也可以不指定,而且可以结合具体的场景进行使用,但是我们声明的时候就只能使用一种

    //一般封装的都是设置私有化的构造方法,然后提供一个公共的访问方法就可以,很类似于单例模式的思想
    //下面的这些构造方法适用于不同的业务场景,具体的看情况再使用

    //适用于只有状态的情况
    private ServerResponse(int status){
        this.status=status;
    }

    //适用于有状态和消息的情况
    public ServerResponse(int status, String msg){
        this.status=status;
        this.msg=msg;
    }

    //适用于有状态和数据的情况
    public ServerResponse(int status, T data){
        this.status=status;
        this.data=data;
    }

    //适用于有状态,消息和数据的情况
    private ServerResponse(int status, String msg,T data){
        this.status=status;
        this.msg=msg;
        this.data=data;
    }

    //根据responseCode这个枚举类里面设定的状态码进行状态判断

    //使之不在 json 序列化结果当中
    @JsonIgnore    //这里添加了json的忽略序列化,就使得它不会出现在json里面
    public boolean isSuccess(){
        return this.status ==ResponseCode.SUCCESS.getCode();
    }

    //根据私有字段提供的公共访问方法
    public int getStatus(){
        return status;
    }

    public String getMsg(){
        return msg;
    }

    public T getData(){
        return data;
    }

    //根据前面的构造方法来返回成功的代码

    public static <T> ServerResponse<T> createBySuccess(){
        return new ServerResponse<T>(ResponseCode.SUCCESS.getCode());
    }

    public static <T> ServerResponse<T> createBySuccessMassage(String msg){
        return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(),msg);
    }

    public static <T> ServerResponse<T> createBySuccess(T data){
        return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(),data);
    }


    public static <T> ServerResponse<T> createBySuccess(String msg,T data){
        return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(),msg,data);
    }


    //根据前面的构造方法来返回失败的代码
    public static <T> ServerResponse<T> createByError(){
        return new ServerResponse<T>(ResponseCode.ERROR.getCode(),ResponseCode.ERROR.getDesc());
    }

    public static <T> ServerResponse<T> createByErrorMessage(String errorMessage){
        return new ServerResponse<T>(ResponseCode.ERROR.getCode(),errorMessage);
    }

    public static <T> ServerResponse<T> createByErrorCodeMessage(int errorCode,String errorMessage){
        return new ServerResponse<T>(errorCode,errorMessage);
    }

}

为了更好的对响应的状态进行判断,我们在common包下面新建一个ResponseCode.java文件(这是一个枚举类),里面的代码如下:

package top.store.common;

/**
 * @author lenovo
 * */
public enum ResponseCode {  //响应编码的枚举类
    SUCCESS(0,"success"),   //成功
    ERROR(1,"error"),   //失败
    NEED_LOGIN(10,"NEED_LOGIN"),  //需要登录
    ILLEGAL_ARGUMENT(2,"ILLEGAL_ARGUMENT");   //参数错误


    private final int code;
    private final String desc;

    ResponseCode(int code,String desc){
        this.code = code;
        this.desc = desc;
    }

    public int getCode(){
        return code;
    }

    public String getDesc(){
        return desc;
    }
}

接着我们在common包里面新建一个Const.java文件,里面存放一些公共的信息,里面的代码如下:

package top.store.common;

public class Const {
    public static final String CURRENT_USER = "currentUser";
}

在前面我们用到了checkUsername这个方法,因此我们需要在UserMapper.xml里面进行配置:

 <select id="checkUsername" resultType="int" parameterType="string">
     select count(1) from store_user
     where username = #{username}
    </select>

    <select id="selectLogin" resultMap="BaseResultMap" parameterType="map">
        SELECT
        --     *???//这里最好不要使用*,因为会查到很多你不需要的东西
        <include refid="Base_Column_List" />
        from store_user
        where username = #{username}
        and password = #{password}
    </select>

注意格式:一般就是里面的id就是下面的方法名称,resultType就是方法的返回类型,parameterType就是里面参数的数据类型。对于多个参数的方法,依旧还是那样的。

int checkUsername(String username);    //这里找不到对应的实现类,所以应该去UserMapper.xml里面进行配置

 //这里找不到对应的实现类,所以应该去UserMapper.xml里面进行配置
User selectLogin(@Param("username") String username, @Param("password") String password);   //注意mybatis传递多个参数时,需要使用param注解

这样我们的用户登录模块就实现了,感谢你的赏阅!

上一篇 下一篇

猜你喜欢

热点阅读