登录和登录拦截实现

一:前端登录按钮绑定事件发送请求后端接口

//登录按钮
      login(ev) {
        //点击登录校验
        this.$refs.ruleForm2.validate((valid) => {
          //校验成功
          if (valid) {
            this.logining = true;  //显示忙等框
            //发送axios请求
            this.$http.post("/login/account",this.ruleForm2).then(res=>{
              this.logining = false;

二:后端接口接收到前端发送的请求与数据

package cn.ybl.user.controller;

import cn.ybl.basic.exception.BusinessException;
import cn.ybl.basic.util.AjaxResult;
import cn.ybl.user.dto.LoginDto;
import cn.ybl.user.service.ILoginInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

/**
 * @Author Mr.Yang
 * @createTime 2022/7/29 17:09
 * @Describe 登录接口
 */
@RestController
@RequestMapping("/login")
public class LoginController {

    @Autowired
    private ILoginInfoService loginInfoService;

    /**
     * 账号登录
     * @param loginDto
     * @return AjaxResult
     */
    @PostMapping("/account")
    public AjaxResult accountLogin(@RequestBody LoginDto loginDto){
        try {
            Map<String, Object> map = loginInfoService.accountLogin(loginDto);
            return AjaxResult.getResult().setResultObj(map);
        }
        catch (BusinessException e) {
            e.printStackTrace();
            return AjaxResult.getResult().setMsg(e.getMessage());
        }
        catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.getResult().setMsg("系统异常,请稍后重试");
        }
    }
}

三:业务层对传递过来的数据进行校验,并与数据库中的数据进行比对,比对失败直接抛异常终止程序运行,比对成功则通过UUID生成一个token,与查询出来的对象一起设置进Redis,并将token和对象返回给前端,为了安全,要把密码和盐值去掉

package cn.ybl.user.service.impl;

import cn.ybl.basic.exception.BusinessException;
import cn.ybl.basic.service.impl.BasicServiceImpl;
import cn.ybl.basic.util.Md5Utils;
import cn.ybl.user.domain.LoginInfo;
import cn.ybl.user.dto.LoginDto;
import cn.ybl.user.mapper.LoginInfoMapper;
import cn.ybl.user.service.ILoginInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @Author Mr.Yang
 * @createTime 2022/7/26 10:45
 * @Describe 登录用户信息表Service实现类
 */
@Service
public class LoginInfoServiceServiceImpl extends BasicServiceImpl<LoginInfo> implements ILoginInfoService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private LoginInfoMapper loginInfoMapper;

    /**
     * 修改激活属性
     * @param id
     */
    @Override
    public void activation(Long id) {
        loginInfoMapper.activation(id);
    }

    /**
     * 根据手机号或邮箱和类型登录
     * @param loginDto
     * @return Map
     */
    @Override
    public Map<String, Object> accountLogin(LoginDto loginDto) {
        //判空
        if(StringUtils.isEmpty(loginDto.getPhone())){
            throw new BusinessException("请输入账号");
        }
        if(StringUtils.isEmpty(loginDto.getPassword())){
            throw new BusinessException("请输入密码");
        }
        if(StringUtils.isEmpty(loginDto.getLoginType())){
            throw new BusinessException("请选择登录身份");
        }
        //校验账号【根据用户名、手机号和type查询】
        LoginInfo loginInfo = loginInfoMapper.accountLogin(loginDto);
        if(loginInfo==null){
            throw new BusinessException("用户或密码错误");
        }
        //判断账号是否激活
        if("0".equals(loginInfo.getDisable())){
            throw new BusinessException("您的账号已禁用,请联系管理员");
        }
        //验证密码是否正确
        String password = loginDto.getPassword();    //获取输入的密码
        String salt = loginInfo.getSalt();           //获取查询出来的对象的盐值
        String md5Pwd = Md5Utils.encrypByMd5(password + salt);      //加密加盐进行对比密码
        if(!loginInfo.getPassword().equals(md5Pwd)){
            throw new BusinessException("用户或密码错误");
        }
        //通过uuid获取一个随机的key
        String token = UUID.randomUUID().toString();
        //将对象和key存入redis并设置30分钟有效
        redisTemplate.opsForValue().set(token,loginInfo,30, TimeUnit.MINUTES);
        Map<String, Object> map = new HashMap<>();
        map.put("token",token);
        loginInfo.setPassword(null);
        loginInfo.setSalt(null);
        map.put("logininfo",loginInfo);
        //将map返回给前端
        return map;
    }
}

四:前端判断返回的数据是否登录成功,成功则拿到返回的对象和token后通过JSON.stringify转为json对象存入localStorage【拦截器要用,判断登录状态】

//登录按钮
      login(ev) {
        //点击登录校验
        this.$refs.ruleForm2.validate((valid) => {
          //校验成功
          if (valid) {
            this.logining = true;  //显示忙等框
            //发送axios请求
            this.$http.post("/login/account",this.ruleForm2).then(res=>{
              this.logining = false;
              //登陆成功
              if(res.data.success){
                this.$message.success("登录成功,即将跳转后台首页")
                //将后台传递的resultObj的token和登录对象信息存入localStorage
                var token = res.data.resultObj.token
                var logininfo = res.data.resultObj.logininfo
                localStorage.setItem("token",token)
                localStorage.setItem("logininfo",JSON.stringify(logininfo))
                //跳转页面
                this.$router.push({ path: '/echarts' });
              }
              //登陆失败
              else{
                this.$message.error(res.data.msg)
              }
            }).catch(res=>{
              this.$message.error("系统繁忙")
            })
          }
          //校验失败
          else {
            this.$message({
              message:"请检查你输入的数据!",
              type:"error",
            })
            return false
          }
        });
      }

到此登录完成

五:前端——axios请求拦截器

  请求拦截器拦截一切axios请求,它的任务是从localStorage中获得token并将它放进请求头中,以供后端取出做校验

//===============================axios请求拦截器【发请求前将token加到请求头】===============================
axios.interceptors.request.use(res=>{
  let token = localStorage.getItem("token");
  if(token){
    res.headers["token"] = token;
  }
  return res;
},error => {
  Promise.reject(error)
})

六:前端——axios响应拦截器

  响应拦截器在后端响应数据后执行,它的任务是比对后端MVC拦截器拦截到的非法请求返回的json数据,作出反应

//===============================axios响应拦截器===============================
axios.interceptors.response.use(res => {
  //没登陆或者过期
  if (false === res.data.success || "noLogin" === res.data.msg) {
    localStorage.removeItem("token");
    localStorage.removeItem("logininfo");
    router.push({path: '/login'});
  }
  return res;
},error => {
  Promise.reject(error)
})

七:前端——路由拦截器

  路由拦截器在前端发送请求时拦截,它从localStorage中获取token,存在则放行,不存在则拦截请求

//===============================路由拦截器【静态资源拦截器】===============================
router.beforeEach((to, from, next) => {
  //跳转登录注册页面清空localStorage
  if (to.path == '/login' || to.path == "/register") {
    localStorage.removeItem("token");
    localStorage.removeItem("logininfo");
    next();//放行
  }else{
    //判断是否登录
    let logininfo = localStorage.getItem('logininfo');
    if (logininfo) {
      next();
    } else {
      next({path: '/login'});//跳转到login
    }
  }
})

八:后端——SpringMVC拦截器

  Springboot实现SpringMVC拦截器由拦截器类和配置类组成

  拦截器类:【负责拦截逻辑处理,从请求头中获取头信息,通过Redis判断是否失效,失效则返回json,没失效则刷新过期时间】

package cn.ybl.basic.interceptor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;

/**
 * @Author Mr.Yang
 * @createTime 2022/7/30 14:49
 * @Describe 登录拦截器
 */
@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //获取请求头中的数据
        String token = request.getHeader("token");
        if(token!=null){
            //根据请求头key在Redis中取值
            Object logininfo = redisTemplate.opsForValue().get(token);
            if(logininfo!=null){    //说明没过期
                //重新设置过期时间
                redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);
                //放行
                return true;
            }
        }
        //请求头中没有数据或者登录信息过期了==============进行拦截
        //手动拼接一个json数据返回给前端
        response.setContentType("application/json;charset=utf8");
        response.getWriter().write("{\"success\":false,\"msg\":\"nologin\"}");
        return false;
    }
}

 拦截器配置类:【负责配置拦截的具体请求,相当于SSM的xml】

package cn.ybl.basic.interceptor.config;

import cn.ybl.basic.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @Author Mr.Yang
 * @createTime 2022/7/30 15:38
 * @Describe 拦截器配置类【配置拦截的请求】
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                //拦截所有请求
                .addPathPatterns("/**")
                //放行登录请求
                .excludePathPatterns("/logins/**")
                //放行注册请求
                .excludePathPatterns("/register/**")
                //放行fastdfs请求
                .excludePathPatterns("/fastDfs/**")
                //放行图形验证码请求
                .excludePathPatterns("/verifyCode/**")
                //放行店铺入驻
                .excludePathPatterns("/shop/settlement")
                //放行swagger
                .excludePathPatterns("//swagger-resources/**\", \"/webjars/**\", \n" +
                        "                                 \"/v2/**\", \"/swagger-ui.html/**");
    }
}

到此登录拦截完成

posted @ 2022-07-30 17:47  yyybl  阅读(75)  评论(0)    收藏  举报