用户模块

功能:

1.登录

controller层实现:

从前端获取传入的用户名和密码,然后通过service进行具体的业务层校验,如果通过校验,则将此时的sessionId作为token值写入cookie中,然后将登录信息写入redis中,key是sessionId,value是具体的登录信息,并设置有效期。

 1     @RequestMapping(value = "login.do", method = RequestMethod.POST)
 2     @ResponseBody
 3     public ServerResponse<User> login(String username, String password, HttpSession session, HttpServletResponse httpServletResponse) {
 4         ServerResponse<User> response = iUserService.login(username, password);
 5         if(response.isSuccess()) {
 6             CookieUtil.writeLoginToken(httpServletResponse, session.getId());
 7             //sessionId是key,用户的登录信息是value,存在redis中
 8             //sessionId是tomcat中自动生成的,针对当前项目,每启动一次,sessionId就变一次
 9             //设置session超时时间
10             RedisShardedPoolUtil.setEx(session.getId(), JsonUtil.obj2String(response.getData()), Const.RedisCacheExtime.REDIS_SESSION_EXTIME);
11 
12         }
13         return response;
14     }
View Code

service层实现:

先校验用户名是否存在,如果存在,再将输入的密码进行md5加密,再去校验密码是否正确,再返回校验结果。

 1     public ServerResponse<User> login(String username, String password) {
 2         //校验用户名是否存在,如果存在,查询结果会返回1,因为用户名唯一;否则返回0
 3         int resultCount = userMapper.checkUsername(username);
 4         if (resultCount == 0) {
 5             return ServerResponse.createByErrorMessage("用户名不存在");
 6         }
 7         //密码登录MD5,将密码加密后再与数据库中的密码进行比对,因为数据库中存的不是明文密码,是md5加密后的密码
 8         String md5Password = MD5Util.MD5EncodeUtf8(password);
 9         //这里不是根据用户名校验密码是否正确,而是将用户名和密码同时传入dao层,在数据库中一起校验是否有当前用户存在,如果存在表明校验通过返回该用户对象
10         User user = userMapper.selectLogin(username, md5Password);
11         if (user == null) {
12             return ServerResponse.createByErrorMessage("密码错误");
13         }
14         //将返回给controller的密码置空,是为了不让其显示出来
15         user.setPassword(StringUtils.EMPTY);
16         //校验通过,返回user对象给controller进行显示
17         return ServerResponse.createBySuccess("登录成功", user);
18     }
View Code

2.注册

controller层实现:

1 @RequestMapping(value = "register.do", method = RequestMethod.POST)
2     @ResponseBody
3     public ServerResponse<String> register(User user) {
4         return iUserService.register(user);
5     }
View Code

service层实现:

先校验用户名和email的唯一性,这个唯一性是由service层保证的,而不是数据库层面由unique决定的,然后对密码进行加密后,进入dao层插入数据库进行持久化操作。

 1     public ServerResponse<String> register(User user) {
 2         //因为下面的校验用户名和email具有相同的逻辑,所以可以集成放在一个函数中,即checkValid函数,用第二个参数辨别传入的是用户名还是email,然后再校验
 3         //校验用户名是否已经存在
 4         ServerResponse validResponse = this.checkValid(user.getUsername(), Const.USERNAME);
 5         if (!validResponse.isSuccess()) {
 6             return validResponse;
 7         }
 8         //校验email是否已经存在
 9         validResponse = this.checkValid(user.getEmail(), Const.EMAIL);
10         if (!validResponse.isSuccess()) {
11             return validResponse;
12         }
13         //设置用户角色
14         //这里是由前端决定注册的是普通用户还是管理员角色,因为不同的角色前端跳转的后台url是不一样的,以此后台来区分角色,所以这里直接将角色赋为customer。
15         user.setRole(Const.Role.ROLE_CUSTOMER);
16         //MD5加密,加密后再存入数据库       user.setPassword(MD5Util.MD5EncodeUtf8(user.getPassword()));
17         //进入dao层进行插入操作
18         int resultCount = userMapper.insert(user);
19         if (resultCount == 0) {
20             return ServerResponse.createByErrorMessage("注册失败");
21         }
22         return ServerResponse.createBySuccessMessage("注册成功");
23     }
View Code

3.提交问题答案:

controler层实现:

1     @RequestMapping(value = "forget_check_answer.do", method = RequestMethod.POST)
2     @ResponseBody
3     public ServerResponse<String> forgetCheckAnswer(String username, String question, String answer) {
4         return iUserService.checkAnswer(username, question, answer);
5     }
View Code

service层实现:

先校验问题的答案是否正确,如果正确,则生成一个UUID作为token值,存入redis中缓存,并设置有效期,然后将token值返回。

 1     public ServerResponse<String> checkAnswer(String username, String question, String answer) {
 2         //校验问题答案是否正确
 3         int resultCount = userMapper.checkAnswer(username, question, answer);
 4         if(resultCount > 0) {
 5             //说明问题及问题答案是这个用户的,并且是正确的
 6             //利用UUID创建无重复token
 7             String forgetToken = UUID.randomUUID().toString();
 8             //把token放入本地cache中,然后设置其有效期
 9         //    TokenCache.setKey(TokenCache.TOKEN_PREFIX + username, forgetToken);
10 
11             RedisShardedPoolUtil.setEx(Const.TOKEN_PREFIX + username, forgetToken, 60 * 60 * 12);
12 
13             //为什么这里调用的是泛型参数函数而不是string参数函数
14             //因为函数名不同啊。。。
15             return ServerResponse.createBySuccess(forgetToken);
16         }
17         return ServerResponse.createByErrorMessage("问题的答案错误");
18     }
View Code

4.重置密码:

  1)登录状态下重置密码

  2)忘记密码后重置密码:如果不传入token,则很容易发生横向越权,即可以传任意用户名,然后将其密码重置,不需要通过问题校验。

controler层实现:

1     @RequestMapping(value = "forget_reset_password.do", method = RequestMethod.POST)
2     @ResponseBody
3     public ServerResponse<String> forgetResetPassword(String username, String passwordNew, String forgetToken) {
4         return iUserService.forgetResetPassword(username, passwordNew, forgetToken);
5     }
View Code

service层实现:

先校验token是否正确传入,然后校验用户名是否存在(因为是忘记密码重置),然后从redis中取出缓存的token值进行比对,如果相同则说明是同一用户,对新密码进行加密,然后持久化到数据库。

 1     public ServerResponse<String> forgetResetPassword(String username, String passwordNew, String forgetToken) {
 2         //校验token是否已经传递过来
 3         if(StringUtils.isBlank(forgetToken)) {
 4             return ServerResponse.createByErrorMessage("参数错误,token需要传递");
 5         }
 6         //校验用户名是否存在
 7         ServerResponse validResponse = this.checkValid(username, Const.USERNAME);
 8         if(validResponse.isSuccess()) {
 9             //用户不存在
10             return ServerResponse.createByErrorMessage("用户不存在");
11         }
12         //从cache中获取token,根据key拿到value值
13     //    String token = TokenCache.getKey(TokenCache.TOKEN_PREFIX + username);
14 
15         String token = RedisShardedPoolUtil.get(Const.TOKEN_PREFIX + username);
16 
17         //校验cache中的token是否有效
18         if(StringUtils.isBlank(token)) {
19             return ServerResponse.createByErrorMessage("token无效或者过期");
20         }
21         //校验传入的forgetToken和token是否相同
22         //这里用stringUtils可以避免string.equals中string出现null的问题,即使这里传入null也是可以接受的,不会报错
23         if(StringUtils.equals(forgetToken, token)) {
24             //将新输入的密码进行md5加密后再更新到数据库中
25             String md5Password = MD5Util.MD5EncodeUtf8(passwordNew);
26             int rowCount = userMapper.updatePasswordByUsername(username, md5Password);
27             if(rowCount > 0) {
28                 return ServerResponse.createBySuccessMessage("修改密码成功");
29             }
30         }
31         else {
32             return ServerResponse.createByErrorMessage("token错误,请重新获取重置密码的token");
33         }
34         return ServerResponse.createByErrorMessage("修改密码失败");
35     }
View Code

5.获取用户信息

controller层实现:

先从cookie中获取是否登录信息,然后拿到这个sessionId后,去redis中根据这个sessionId,拿到user对象,根据userId从数据库中获取到具体的用户信息,返回给前端。

 1     @RequestMapping(value = "get_information.do", method = RequestMethod.POST)
 2     @ResponseBody
 3     public ServerResponse<User> getInformation(HttpServletRequest request) {
 4 
 5         String loginToken = CookieUtil.readLoginToken(request);
 6         if(StringUtils.isEmpty(loginToken)) {
 7             return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户的信息");
 8         }
 9         String userJsonStr = RedisShardedPoolUtil.get(loginToken);
10         User currentUser = JsonUtil.string2Obj(userJsonStr, User.class);
11 
12         if(currentUser == null) {
13             return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(), "未登录,需要强制登录status=10");
14         }
15         return iUserService.getInformation(currentUser.getId());
16     }
View Code

6.更新用户信息

7.退出登录

掌握:

1.横向越权、纵向越权安全漏洞

2.MD5明文加密及增加salt盐值

3.Guava缓存的使用

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

 1 /**
 2  * Created by cq on 2017/10/31.
 3  */
 4 @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
 5 //保证序列化json的时候,如果是null的对象,key也会消失
 6 //比如在创建失败的时候,只需要将status和msg显示出来,但是如果不加上面的注解会将data也显示出来,其值会显示null
 7 public class ServerResponse<T> implements Serializable{
 8 
 9     private int status;
10     private String msg;
11     private T data;
12 
13     private ServerResponse(int status) {
14         this.status = status;
15     }
16     //如果第二个参数不是string类型调用下面这个构造器
17     private ServerResponse(int status, T data) {
18         this.status = status;
19         this.data = data;
20     }
21     //如果第二个参数是string类型调用下面这个构造器
22     private ServerResponse(int status, String msg) {
23         this.status = status;
24         this.msg = msg;
25     }
26     private ServerResponse(int status, String msg, T data) {
27         this.status = status;
28         this.msg = msg;
29         this.data = data;
30     }
31 
32     @JsonIgnore
33     //使之不在json序列化结果当中
34     //判断响应是否成功
35     public boolean isSuccess() {
36         return this.status == ResponseCode.SUCCESS.getCode();
37     }
38 
39     //下面三个get开头的public方法在json序列化时都会显示出来供前端看到
40     //数据格式显示也就如下:
41     /*
42     * {status:xxx,
43     * msg:xx,
44     * data:xxx}
45     * */
46     public int getStatus() {
47         return status;
48     }
49 
50     public T getData() {
51         return data;
52     }
53 
54     public String getMsg() {
55         return msg;
56     }
57 
58     //成功
59     //返回值是泛型<T>,传入0表示创建是成功的
60     public static <T> ServerResponse<T> createBySuccess() {
61         return new ServerResponse<T>(ResponseCode.SUCCESS.getCode());
62     }
63 
64     //创建成功传入一个msg再返回
65     public static <T> ServerResponse<T> createBySuccessMessage(String msg) {
66         return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(), msg);
67     }
68 
69     //创建成功传入data再返回
70     public static <T> ServerResponse<T> createBySuccess(T data) {
71         return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(), data);
72     }
73 
74     //如果参数是string类型,但是应该把其赋给data而不是msg该怎么做?
75     //调用下面的public方法同时传入msg和data即可
76     //创建成功传入一个msg和data再返回
77     public static <T> ServerResponse<T> createBySuccess(String msg, T data) {
78         return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(), msg, data);
79     }
80 
81     //失败
82     public static <T> ServerResponse<T> createByError() {
83         return new ServerResponse<T>(ResponseCode.ERROR.getCode(), ResponseCode.ERROR.getDesc());
84     }
85 
86     //创建失败传入一个错误提示信息errorMessage
87     public static <T> ServerResponse<T> createByErrorMessage(String errorMessage) {
88         return new ServerResponse<T>(ResponseCode.ERROR.getCode(), errorMessage);
89     }
90 
91     //创建失败手动传入一个code和errorMessage
92     public static <T> ServerResponse<T> createByErrorCodeMessage(int errorCode, String errorMessage) {
93         return new ServerResponse<T>(errorCode, errorMessage);
94     }
95 
96 }
View Code

5.session的使用

6.json注解使用

原文地址:https://www.cnblogs.com/cing/p/7806324.html