SpringBoot+Vue前后端分离实战(用户注册登录)

@[TOC]

前言

昨天抽空终于把后端架起来了,准备开始编写后端,结果由于是第一次做前后端分离,搞的我闹了不少洋相,查了半天资料发现是个小细节没搞好,气死我。

注册

既然要登录那必然是先少不了注册,注册之后才能去登录呀。 那么咱们这个毕竟是作为一个前后端分离的项目,所以的话注册部分的逻辑也是分两个部分,一个是前端部分,一个是后端部分。 在这里插入图片描述

前端部分逻辑

前端其实就两个。 在这里插入图片描述

关于这部分的话是前端自己生成了验证码,前端校验账号密码长度是否合法。 在这里插入图片描述 关于这部分前端代码见:Vue实战开发(一)验证码与登录页面

发送请求

这个也是比较重要的一部分,我这边使用的是 Axios 发送请求。但是这里一定要注意一个细节 Axios发送的请求参数是封装在requestbody里面的,这个关系到我们后面怎么拿数据,先前我就是不知道这个,搞得我一直接收不到参数 不过在此之前我们必须做一件事前,那就是跨域处理 这一块我是在前端做的,后端没去做,为了保证我后端的安全。 在这里插入图片描述

proxyTable: {

      '/boot': {
        target: 'http://localhost:8000/',
        changeOrigin: true,
        pathRewrite: {
          '^/boot': ''
        }
      }

    },
复制代码

target 是你的服务器,/boot 是指代那个主机。 那么现在我们开始发送请求 在这里插入图片描述

    Register(){
      this.axios({
        url: "/boot/regist",
        method: 'post',
        data: {
          username:  this.formRegist.username,
          password: this.formRegist.password.toLowerCase(),
        }
      }).then(res =>{
        this.flag = res.data.flag;
        if(this.flag =='1'){
          alert("注册成功")
          this.$router.push("/login")
        }
        else {
          alert("注册失败用户名已存在!")
        }
      })
复制代码

在这里插入图片描述 在这里插入图片描述

后端处理

这里的话我要说一下那个参数,那个flag就是来说明用户是否注册成功的,如果成功 flag 为1 反之为 0

现在把目光移步到 springboot 这边的逻辑其实很简单,就是检查用户名是否存在,存在就不注册,然后返回flag

@Controller
public class UserRegist {
    @Autowired
    UserService userService;
    @Autowired
    RegistMessage registMessage;
    @ResponseBody
    @PostMapping("/regist")
    public RegistMessage SaveUser(@RequestBody Map<String, Object> usermap){
        String username = (String) usermap.get("username");
        String password = (String) usermap.get("password");

        QueryWrapper<User> UsersWrapper = new QueryWrapper<>();
        UsersWrapper.eq("UserName",username);
        if(userService.IsExitsUser(UsersWrapper)){
            registMessage.setFlag(0);
            return registMessage;
        }
        else {
            User user = new User();
            user.setUserName(username);
            user.setUserPassword(password);
            userService.SaveUser(user);
            registMessage.setFlag(1);
            return registMessage;
        }


    }
}

复制代码

在这里插入图片描述

之后在数据库就能见到注册好的账号密码了。

再然后就是前端重新拿到数据 在这里插入图片描述 判断情况。

登录

同样的登录也要进行区分,但是这里面稍微麻烦的是这边我们还要生成一个token用来记录用户的登录状态,并且由于是前后端分离,所以我们不能直接用session 或者 cookie ,像先前用Django那样直接request.session,或者 springboot 那样直接 Httpsession 。这边我们需要自己去加密token,并且让前端保存token。

这边我们还是先看到前端吧。 前端唯一要做的就是发送用户和密码,然后接收token。以后请求敏感页面的时候就会带入token。

前端获取token

核心的话就只有这个

 logincount(){
      this.axios({
        url: "/boot/login",
        method: 'post',
        headers: { "Huterox": "hello" },
        data: {
          username:  this.formLogin.username,
          password: this.formLogin.password.toLowerCase(),
        }
      }).then(res =>{
        this.formLogin.success = res.data.success
        this.formLogin.token = res.data.token
        if(this.formLogin.success =='1'){
          //设置token七天过期
          localStorage.setExpire("token",this.formLogin.token,1000*60*60*24*7);
          alert("登录成功~")
          this.$router.push("/space")
        }
        else {
          alert("用户名或密码错误!")
        }
      })
    },
复制代码

那个success和flag是一个东西。

前端token状态管理

由于没有cookie所以我们这边需要自己保存一下token,但是由于vuex 的session在页面关闭或者刷新之后就没了所以只能用 localStorage 但是这玩意的话没有办法设置过期时间所以还要自己动手处理一下。 封装 localStorage的方法。

在这里插入图片描述

Storage.prototype.setExpire=(key, value, expire) =>{
  let obj={
    data:value,
    time:Date.now(),
    expire:expire
  };
  localStorage.setItem(key,JSON.stringify(obj));
}

Storage.prototype.getExpire= key =>{
  let val =localStorage.getItem(key);
  if(!val){
    return val;
  }
  val =JSON.parse(val);
  if(Date.now()-val.time>val.expire){
    localStorage.removeItem(key);
    return null;
  }
  return val.data;
}


复制代码

之后敏感页面自己在判断有木有token就好了。

localStorage.getExpire("token")

后端处理

这一套在前端处理起来是相对容易,那么接下来就是后端了。当然在后端主要是涉及到生成token和校验token。毕竟前端只需要判断有误token即可,后端是要验证的,至于怕不怕有人恶意篡改,只要你改了,我后端验证就过不了,我后面引入Redis就专门记录这种情况,正常情况下token不会被恶意篡改,也就是能够通过校验,一旦过不了那很大概率是被恶意篡改了,那就拉入监控名单,次数超过限制关进小黑屋,或者前端调用浏览器定位。安全这块我本人是比较注重的,比较玩过一段时间的信安。

先回到这里,我们继续说说下面怎么处理。

用户登录

在这里插入图片描述

之后前端查看返回结果。成功了就有token

生成token

这里用到 jwt

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.6.0</version>
        </dependency>
复制代码

package com.huterox.whiteholeboot.utils.TokenProccessor;


/**
 * 生成token
 */

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.huterox.whiteholeboot.Dao.Mapper.UserMapper.UserServer.UserService;
import com.huterox.whiteholeboot.Dao.Pojo.UserPojo.User;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Autowired;

public class TokenProccessor {


    private static UserService userService=new UserService();
    private static final long EXPIRE_TIME=60*60*1000*24*7; //过期时间7天
    private static final String KEY = "huterox"; //加密秘钥

    /**
     * 生成token
     * 由于只有当账号密码正确之后才会生成token所以这边只需要用户名进行识别
     * @param userName  用户名
     * @return
     */
    public static String createToken(String userName){
        Map<String,Object> header = new HashMap();
        header.put("typ","JWT");
        header.put("alg","HS256");
        JwtBuilder builder = Jwts.builder().setHeader(header)
                .setExpiration(new Date(System.currentTimeMillis()+EXPIRE_TIME))
                .setSubject(userName)//设置信息,也就是用户名
                .setIssuedAt(new Date())
                .signWith(SignatureAlgorithm.HS256,KEY);//加密方式
        return builder.compact();
    }

    /**
     * 验证token是否有效
     * @param token  请求头中携带的token
     * @return  token验证结果  2-token过期;1-token认证通过;0-token认证失败
     */
    public static int verify(String token){
        Claims claims = null;
        try {
            //token过期后,会抛出ExpiredJwtException 异常,通过这个来判定token过期,
            claims = Jwts.parser().setSigningKey(KEY).parseClaimsJws(token).getBody();
        }catch (ExpiredJwtException e){
            return 2;
        }
        //从token中获取用户名,当用户查询通过后即可
        String username = claims.getSubject();
        User user = userService.selectUserByName(username);
        if(user != null){
            return 1;
        }else{
            return 0;
        }
    }

}

复制代码

拦截器设置

这里的话是需要用到拦截器了,主要是后面的处理,登录之后某些请求时需要token的,那么这里就要检验。 在这里插入图片描述

package com.huterox.whiteholeboot.Config;

import com.alibaba.fastjson.JSON;

import com.huterox.whiteholeboot.utils.TokePassJson.TokenPassJson;
import com.huterox.whiteholeboot.utils.TokenProccessor.TokenProccessor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

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

@Component
public class TokenInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler)throws Exception{
        if(request.getMethod().equals("OPTIONS")){
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        }
        response.setCharacterEncoding("utf-8");
        String token = request.getHeader("token");
        int result = 0;
        if(token != null){
            result = TokenProccessor.verify(token);
            if(result == 1){
                System.out.println("通过拦截器");
                return true;
            }

        }
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        try{
            TokenPassJson json = new TokenPassJson();
            json.setSuccess(result);//0表示验证失败,2表示过期
            response.getWriter().append(JSON.toJSONString(json));
            System.out.println("认证失败,未通过拦截器");
        }catch (Exception e){
            e.printStackTrace();
            response.sendError(500);
            return false;
        }
        return false;
    }
}

复制代码
package com.huterox.whiteholeboot.Config;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;

@Configuration
public class TokenConfig implements WebMvcConfigurer {




//    @Override
//    public void addCorsMappings(CorsRegistry registry) {
//        registry.addMapping("/**")
//                .allowCredentials(true)
//                .allowedHeaders("*")
//                .allowedMethods("*")
//                .allowedOrigins("*");
//
//    }
   // 跨域,我们前端做了跨域所以的话后端不用,也是为了安全,不能什么阿猫阿狗都过来访问

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        List<String> excludePath = new ArrayList<>();
        //排除拦截,除了注册登录(此时还没token),其他都拦截
        excludePath.add("/regist");  //登录
        excludePath.add("/login");     //注册
        excludePath.add("/static/**");  //静态资源
        excludePath.add("/assets/**");  //静态资源

        registry.addInterceptor(new TokenInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns(excludePath);
        WebMvcConfigurer.super.addInterceptors(registry);
    }


}

复制代码

之后就可happy访问了。

总结

进度有点慢,没办法先磨合一些。此为如果觉得本文不错请给个赞赞~(白嫖可耻,尤其是收藏了还不敢赞赞(下次 收藏数>点赞 ?收费:免费))

猜你喜欢

转载自juejin.im/post/7041832284848128031