springBoot+mybatis-plus+MySQL实现前后端登录注册案例(跟着做完直接成为老师眼中的好学生^_^)

postman测试结果(示例):

注册失败返回值:

后端可以判断账号是否被注,并返回出结果

登录错误返回值:

登录错误会返回用户名错误还是其他问题导致用户登录不上去,用于精准的提示用户注册 

用户登录账号,密码正确但是跨游览器导致token缺失会返回验证码(让用户选择或者填:前端的事情),用于精准的帮助用户找回token从而登录  

 

 

 用户登录账号,密码正确但是跨游览器导致token缺失会返回验证码用户输入验证码后,后端可以判断验证码是否正确。正确则返回token,前端收到toke保存本地即可:

登录成功返回值: 

登录成功会返回一系列的值给予前端一系列的操作。

开发工具:

idel.vsCode,postman,mysql(navicat)

用到的包:

Lombok,BCrypt.druid,jwt,jbcrypt,

   

基于此后端登录注册业务逻辑:

1.注册流程

前端注册时不携带token,后端springboot接收数据对用户名进行加jwt加密返回token操作,密码则进行加密操作存储到数据库(使用BCrypt.hashpw进行加密),

2.登录流程

前端登录发送用户名,密码等数据到后端,使用mp查询数据库中加密的密码和用户名,判断用户名是否一致,后端接收数据然后把前端发来的密码和数据库中的密码用BCrypt.checkpw解密后,判断密码是否一致,确认一致后然后返回token到前端,前端把token存储在本地。基于以上的业务逻辑,主要介绍注册和登录时密码和用户名的前后端校验。

3.注意

1.前端收到token需要把token存储在游览器本地(localstorage),下次请求时,携带token:才能请求成功。

2.前端用户登录和注册校验数据时,用户名不能超过12位,密码不能超过20位,网名不能超过8位最好!否则会引起后端程序崩溃!!!!!!!!!!!!!!

3.
当用户注册过后又换了游览器登录,就会导致token在新的游览器上丢失,这时只要用户输入的账号密码正确,后端会使用mp查询数据库,判断token是否正确,错误返回验证码(
"由于设备更改:返回验证数字"+loginRandon):前端收到数据,让用户选择哪个数字或者让用户填数字,填完后请求即可。然后端判断验证码是否正确,正确则返回数据库中的token和字符串做拼接来传递给前端。(拼接示例:"检测令牌为空:返回令牌"+tokenOne),前端收到数据,把字符串拆掉存储在本地即可(前端需要做非空验证)。不正确则会返回("数字令牌错误!"

4.前端请求不成功解决方案:

    确认环境是否是vue?
    是vue:代理才行能请求成功(安全)
    原生js:创建控制层类下的可跨域配置类修改成另一种类型(下面会发)不做代理也可以请求成                    功

开始编码:

创建mysql数据库和表字符集utf-8

文件目录:

1.创建spting-boot工程,

勾选MybatisDriver,spring-boot(3以下版本),lombok

2.配置坐标(pom.xml):

​
<dependency>
            <groupId>org.mindrot</groupId>
            <artifactId>jbcrypt</artifactId>
            <version>0.4</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

​

3.配置数据库驱动

server:
  port: 81

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shopping_mall_db?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      id-type: assign_id
      table-prefix: tab_
      logic-delete-field: delete
      logic-delete-value: 1
      logic-not-delete-value: 0

4.创建实体类entity(User)

package com.sms.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import lombok.EqualsAndHashCode;

@Data
@EqualsAndHashCode(callSuper = false)
public class User {
    private Long id;
    private String username;
    private String password;
    @TableField(value = "screen_name")
    private String screenName;
    private String token;
    @TableField(exist = false)
    private Integer loginRandom;
}

5.创建Entity 接口继承BaseMapper类

package com.sms.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sms.domain.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserDao extends BaseMapper<User> {
}

5.创建service层接口SysUserservice,QueryTokenService:

SysUserservice类:
public interface SysUserService extends IService<User> {
    /**
     * 用户注册
     * enroll
     * @return
     */
    String enroll(User user);



    /**
     * 用户登录
     * @param
     *  user
     * @return
     */
//    String login(String userName, String password,String token);
    String login(User user);
}
QueryTokenService类:
package com.sms.server;

import com.baomidou.mybatisplus.extension.service.IService;
import com.sms.domain.User;

public interface QueryTokenService extends IService<User> {
    Boolean returnQueryToken(String token);
}

6.创建service层接口实现类SysUserserviceImpl,QueryTokenServiceImpl:

 SysUserserviceImpl 类:

@Service
@Transactional
public class SysUserserviceImpl extends ServiceImpl<UserDao, User> implements SysUserService {
    @Autowired
    private UserDao userDao;
    //写sql语句的类
    //注册的类,注解可以校验是不是父接口的子类
    Integer returnLoginRandom=null;
    @Override
    public String enroll(User user) {
        String username = user.getUsername();
        String password = user.getPassword();
        String screenName = user.getScreenName();
        try {
            if (username == null || password == null) {
                return "账号密码中不能为空";
            }
            QueryWrapper<User> qw = new QueryWrapper<>();
            qw.eq("userName", username);
            User userEnrollEnroll1 = userDao.selectOne(qw);
            if (userEnrollEnroll1 != null) {
                return "已有账号";
            }
        } catch (NullPointerException e) {
            throw new ComFoundException();
        }
        //使用jwt加密用户名来生成随机token
        LoginToken loginToken = new LoginToken();
        String userUserNameJwt = loginToken.returnLogin(username);
        //使用BCrypt对密码进行加密
        String BCryptPassword = BCrypt.hashpw(password, BCrypt.gensalt());
        //开始存储数据
        User userEnrollEnrollSave = new User();
        userEnrollEnrollSave.setUsername(username);
        userEnrollEnrollSave.setScreenName(screenName);
        userEnrollEnrollSave.setPassword(BCryptPassword);
        userEnrollEnrollSave.setToken(userUserNameJwt);
        int insert = userDao.insert(userEnrollEnrollSave);
        if (insert != 1) {
            return "插入失败";
        }
        return userUserNameJwt;
    }
    //登录的类,注解可以校验是不是父接口的子类
    @Override
    public String login(User user) {
        //获取前端的数据
        String username = user.getUsername();
        String password = user.getPassword();
        String token = user.getToken();
        Integer loginRandom=user.getLoginRandom();

        if (username == null || password == null) {
            return "账号不为空";
        }
        //前端无数字验证值走这些代码(默认无值)
        if (loginRandom==null) {
            //判断数据是否无误
            LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
            lqw.eq(User::getUsername,username);
            lqw.select(User::getId, User::getPassword,User::getToken);
            List<User> userList = userDao.selectList(lqw);
            if (userList == null || userList.size() == 0) {
                return "用户名或密码错误";
            }

            //对密码进行解密
            String passwordBCryptCheckpw = null;

            for (User userPassword : userList) {
                passwordBCryptCheckpw = userPassword.getPassword();
            }
            //使用BCrypt.checkpw(password,passwordBCryptCheckpw)来确认数据是否正确
            Boolean flag = BCrypt.checkpw(password, passwordBCryptCheckpw);
            if (flag != true) {
                return "用户名或密码错误";
            }

            //用户跨游览器导致token缺失解决
            String tokenOne=null;
            for (User userToken:userList){
                tokenOne= userToken.getToken();
            }

            if (tokenOne==null||!tokenOne.equals(token)) {
                if (loginRandom==null){
                    RandomNumber randomNumber=new RandomNumber();
                    returnLoginRandom = randomNumber.returnLoginRandom();
                    //如果数据库中查不到token则证明这个用户要么是黑户,要么是工作人员的失误
                    return "由于您的设备更改:返回验证数字"+ returnLoginRandom;
                }
            }
            //使用jwt加密用户名来生成随机token
            LoginToken loginToken = new LoginToken();
            String ReturnUserNameToken = loginToken.returnLogin(password);
            //查询登录的是哪一个人的id;
            String idStr = null;
            for (User userId : userList) {
                idStr = String.valueOf(userId.getId()) + "L";
            }
            long id = Long.parseLong(idStr.replace("L", ""));

            //更新token
            User userPutToken = userDao.selectById(id);
            userPutToken.setToken(ReturnUserNameToken);
            int flags = userDao.updateById(userPutToken);
            if (flags != 1) {
                return "添加失败";
            }
            return ReturnUserNameToken;
        }





        //前端传值loginRandom走这些代码(默认无值)
        if(loginRandom!=null) {
            //判断数据是否无误
            LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
            lqw.eq(User::getUsername,username);
            lqw.select(User::getId, User::getPassword,User::getToken);
            List<User> userList = userDao.selectList(lqw);
            if (userList == null || userList.size() == 0) {
                return "用户名或密码错误";
            }
            //对密码进行解密
            String passwordBCryptCheckpw = null;

            for (User userPassword : userList) {
                passwordBCryptCheckpw = userPassword.getPassword();
            }
            //使用BCrypt.checkpw(password,passwordBCryptCheckpw)来确认数据是否正确
            Boolean flag = BCrypt.checkpw(password, passwordBCryptCheckpw);
            if (flag != true) {
                return "用户名或密码错误";
            }

            //用户跨游览器导致token缺失解决
            // 前端拿到token在进行登录,则能登录成功
            String tokenOne=null;
            for (User userToken:userList){
                tokenOne= userToken.getToken();
            }

            if (tokenOne==null||!tokenOne.equals(token)) {
                if (loginRandom.equals(returnLoginRandom)){
                    returnLoginRandom=null;
                    return "检测令牌为空:返回令牌"+tokenOne;
                }
                if (!loginRandom.equals(returnLoginRandom)){
                    return "数字令牌错误!";
                }

            }

            //使用jwt加密用户名来生成随机token
            LoginToken loginToken = new LoginToken();
            String ReturnUserNameToken = loginToken.returnLogin(password);
            //查询登录的是哪一个人的id;
            String idStr = null;
            for (User userId : userList) {
                idStr = String.valueOf(userId.getId()) + "L";
            }
            long id = Long.parseLong(idStr.replace("L", ""));

            //更新token
            User userPutToken = userDao.selectById(id);

            userPutToken.setToken(ReturnUserNameToken);
            int flags = userDao.updateById(userPutToken);
            if (flags != 1) {
                return "添加失败";
            }
            return ReturnUserNameToken;
        }
        return "错误";
    }

QueryTokenServiceImpl类:
@Service
@Transactional
public class QueryTokenServiceImpl extends ServiceImpl<UserDao,User> implements QueryTokenService {
        @Autowired
        private UserDao userDao;
    @Override
    public Boolean returnQueryToken(String token) {
        QueryWrapper<User> qw=new QueryWrapper<>();
        qw.eq("token",token);
        List<User> userList=userDao.selectList(qw);
        try{
            if (userList==null||userList.size()==0){
                return false;
            }
        }catch (NullPointerException e){
            throw new ComFoundException();
        }
        return true;
    }
}

7.创建统一错误处理类ComFoundException:

package com.sms.server.ex;

public class ComFoundException extends RuntimeException{
    public ComFoundException() {
        super();
    }

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

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

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

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

8.创建工具类来实现返回数据给前端的类和校验类:

RandomNumber:(生成验证码)

public class RandomNumber {
    public int returnLoginRandom(){
        Random random=new Random();
        int loginRandom = random.nextInt(9000) + 1000;
        //生成四位随机数
        return loginRandom;
    }
}
LoginToken:
package com.sms.util.jwttoken;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;


public class LoginToken {
    //登录会用到的工具
    public String returnLogin(String password){
        Date now=new Date();
        //HashMap可以存储键值对
        Map<String,Object> claims=new HashMap<>();
        claims.put("password",password);
        //使用jwt生成器生成Token

        String token= Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(now)
                .signWith(SignatureAlgorithm.HS256,"secret")
                .compact();
        return token;
    }
}
Result:
@Data
public class Result {

    private Integer code;
    private boolean success;
    private String message;
    private Object data;
    private Date date;

    public static Result loginBody(Integer code,
                                   Boolean success,
                                   String message,
                                   Object data,
                                   Date date
                                   ){
        Result result = new Result();
        result.setCode(code);
        result.setSuccess(success);
        result.setData(data);
        result.setMessage(message);
        result.setDate(date);

        return  result;
    }

    public static Result enroll(Integer code,
                                Boolean success,
                                String message,
                                String data,
                                Date date){

        Result result = new Result();
        result.setCode(code);
        result.setSuccess(success);
        result.setData(data);
        result.setMessage(message);
        result.setDate(date);
        return  result;
    }


}

ResultCode

package com.sms.util.ResultLoginCode;

public class ResultCode{
        public static final Integer POST_OK=20011;
        public static final Integer POST_ERR=20010;
        public static final Integer DELETE_OK=20021;
        public static final Integer DELETE_ERR=20020;
        public static final Integer UPDATE_OK=20031;
        public static final Integer UPDATE_ERR=20030;
        public static final Integer GET_OK=20041;
        public static final Integer GET_ERR=20010;
}

9.创建控制层类

1.可跨域配置类(上面提到的请求不成功解决方案!二选一!)

使用:vue做代理的(安全):

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/shop/**")
                .allowedOrigins("http://localhost:8080")
                .allowedMethods("GET","POST","PUT","DELETE")
                .allowedHeaders("Content-Type")
                .allowCredentials(true).maxAge(3600);
    }
}

 原生js的,不需要做代理的:

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedMethods("GET","POST","PUT","DELETE")
                .allowedHeaders("*")
                .allowCredentials(true)
                .maxAge(3600);
    }
}
2.接口类:

@RestController
@RequestMapping("/shop")
public class Controller {

    @Autowired
    private SysUserService sysUserservice;
    @Autowired
    private QueryTokenService queryTokenService;
    @PostMapping("/login")
    public Result getPersonInfo(@RequestBody User user){
        String data = sysUserservice.login(user);
        String newData=null;
        Boolean flag= queryTokenService.returnQueryToken(data);
        Integer code=flag!=true?ResultCode.POST_ERR:ResultCode.POST_OK;
        //解决用户跨游览器导致token缺失;
        String message = null;
        message=flag!=true?data:"恭喜你,登录成功!请稍等,正在进入页面。";
        Boolean flagMessage=data.contains("检测令牌为空:返回令牌");
        Boolean flagMessageNumber=data.contains("由于您的设备更改:返回验证数字");
        if (flagMessageNumber==true){message=data;code=ResultCode.POST_OK;data=null;flag=true;}
        if (flagMessage==true){message=data;code=ResultCode.POST_OK;data=null;flag=true;}
        Date date=new Date();
        Result result = Result.loginBody(code, flag, message,data,date);
        return result;
    }
    //@RequestBody把前端的值转化为json类型
    @PostMapping("/enroll")
    public Result postPersonInfo(@RequestBody User user){
        Integer code;
        String message=null;
        //返回的是token
        String data = sysUserservice.enroll(user);
        code=data!=null? ResultCode.POST_OK:ResultCode.POST_ERR;
        message=code!=ResultCode.POST_OK?data=null:"注册成功!";
        if (data=="已有账号"){message="注册失败:已有账号!";code=ResultCode.POST_ERR;data=null;}
        Boolean success=code!=ResultCode.POST_ERR?true:false;
        Date date=new Date();
        Result result = Result.enroll(code,success,message,data,date);
        return result;
    }
}

恭喜你!!!已成功学完。

最难不过坚持!!!

后续前端会出,敬请关注!^_^

  • 6
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花好月圆呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值