企业中登录中4个拦截器功能详细实现步骤

流程:
以下内容可拷贝直接用。  

1.1登录成功。把token和loginInfo 放进 通过localStorage放进本地浏览器。类似于cookie。

2.1 axois前置拦截。作用就是把登录之后的每个请求头里面加上token 表示是登录过的标识

2.2   后端java代码做拦截配置 。从redis里面取出key--token,如果有值就再次设置30分钟有效期。为null的话,使用输出流返回错误信息。

2.3  axois的后置拦截,实际就是对后端拦截出来的报错信息进行处理

2.4 前端静态资源拦截也可以叫做路由拦截。 如果通过路由访问登录和注册就放行。如果是访问特定资源。就从之前localStorage取信息。也可也从token取。如果有值。那么就放行。否则跳转登录页面。

 1接口先行

 1.1controller层

package cn.wl.user.controller;

import cn.wl.basic.exception.BusinessException;
import cn.wl.basic.util.AjaxResult;
import cn.wl.user.dto.LoginDto;
import cn.wl.user.service.ILoginService;
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;

@RestController
@RequestMapping("/login")
public class LoginController {
    @Autowired
    private ILoginService loginService;
    /**
     * 账号密码登录  前后端公用
     */
    @PostMapping("/account")
    public AjaxResult accountLogin(@RequestBody  LoginDto loginDto){
        try {
            Map<String, Object> map  =loginService.accountLogin(loginDto);
            return AjaxResult.me().setResultObj(map);//返回 token串和 loginInfo基本信息
        } catch (BusinessException e) {
            e.printStackTrace();
            return AjaxResult.fail(e.getMessage());//接受service层抛出的异常
        }catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.fail("铁汁系统错误,请稍后重试");
        }
    }
}

1.2service层

package cn.wl.user.service;

import cn.wl.user.dto.LoginDto;

import java.util.Map;

/**登录。主站和前台公用*/
public interface ILoginService {

    Map<String,Object> accountLogin(LoginDto loginDto);
}

1.3处理实际业务的impl层 

由于是前后端共有登录接口。表涉及用户表,平台员工表,中间表。中间表抽取两种表的登录信息。t通过前端传递的type类型判断是用户还是平台员工登录。0.平台的人。1用户。

package cn.wl.user.service.impl;

import cn.wl.basic.exception.BusinessException;
import cn.wl.basic.util.MD5Utils;
import cn.wl.basic.util.StrUtils;
import cn.wl.user.domain.LoginInfo;
import cn.wl.user.dto.LoginDto;
import cn.wl.user.mapper.LoginInfoMapper;
import cn.wl.user.service.ILoginService;
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;
/**共有登录接口*/
@Service
public class LoginServiceImpl implements ILoginService {
    @Autowired
    private LoginInfoMapper loginInfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    //1效验参数是否为空
    //2查询数据库比对用户名 注意要比对type
    //2.1查询不到。抛出异常
    //3用户是否被禁用
    //4如果查询到了,比对密码  //使用相同盐值,加密登录密码,用来作比对 如果密码不一致,抛异常
    //5 密码一致:存储redis
    //6 返回token和logininfo基本信息给前端 我们不应该把敏感信息返回给前端 密码 salt
    @Override
    public Map<String, Object> accountLogin(LoginDto loginDto) {
       if(StringUtils.isEmpty(loginDto.getUsername())||StringUtils.isEmpty(loginDto.getPassword())){
           throw new BusinessException("请填写完整信息,注意所填内容不能为空");
       }
        LoginInfo loginInfo = loginInfoMapper.loadByUsername(loginDto.getUsername(),loginDto.getType());
       if(loginInfo==null){
           throw  new BusinessException("帐号不存在");
       }
       if(loginInfo.getDisable()==0){
            throw new BusinessException("帐号因违规,已经被封禁,如需申诉,请联系客服");
       }
        String salt = loginInfo.getSalt();
        String s = MD5Utils.encrypByMd5(loginDto.getPassword() + salt);
        if(!(s).equals(loginInfo.getPassword())){
           throw new BusinessException("密码错误");
       }else {
            String token = UUID.randomUUID().toString();
            //把登录成功的用户信息,放进redis服务器。
            redisTemplate.opsForValue().set(token,loginInfo,30, TimeUnit.MINUTES);  //30分钟后过期
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("token",token);
            loginInfo.setSalt(null);
            loginInfo.setPassword(null);
            map.put("loginInfo",loginInfo);
            return map;
        }
    }
}

1.4mapper层

package cn.wl.user.mapper;


import cn.wl.user.domain.LoginInfo;

public interface LoginInfoMapper{
    LoginInfo loadByUsername(String username,Integer type);
    


    /*下面写拓展的方法*/
}
1.5InfoMapper.xml

由于mybaits多参数查询写法有多种。这里我选择了一种写法  0代表username    1代表type

usernam。phone。email为登录中间表的信息。如果匹配了一个自动就说明有这个用户

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.loginInfo//DTD Mapper 3.0//EN"
        "http://mybatis.loginInfo/dtd/mybatis-3-mapper.dtd">

<mapper namespace="cn.wl.user.mapper.LoginInfoMapper">

    <!--LoginInfo loadByUsername(String username,Integer type);-->
    <select id="loadByUsername" resultType="LoginInfo">
        SELECT * FROM t_logininfo where (username=#{0} or phone=#{0} or email=#{0}) AND  
        type = #{1}
    </select>
</mapper>
2.1 用户的domain

User

package cn.wl.user.domain;

import cn.wl.basic.domain.BaseDomain;
import lombok.Data;

import java.util.Date;


@Data
public class User extends BaseDomain {
    //id来自继承 BaseDomain
    private String username;
    private String email;
    private String phone;
    /**盐值*/
    private String salt;
    private String password;
    private Integer state =1; //直接激活
    private String age;
    private Date createtime = new Date();
    private String headImg; 
    private Long logininfo_id; //关联的中间表信息

}
2.2平台的人 
Employee
package cn.wl.org.domain;

import cn.wl.basic.domain.BaseDomain;
import lombok.Data;

@Data
public class Employee extends BaseDomain{
    //id来自继承 BaseDomain
    private String username;
    private String email;
    private String phone;
    /**加密密码*/
    private String salt; //后续使用MD5技术对密码进行加密
    private String password;
    private Integer age;
    private Integer state = 1 ;
    private Long department_id ;
    private Long logininfo_id;
    private Long shop_id;
}
3 User表设计         

CREATE TABLE `t_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `phone` varchar(255) DEFAULT NULL,
  `salt` varchar(255) DEFAULT NULL COMMENT '盐值',
  `password` varchar(255) DEFAULT NULL COMMENT '密码,md5加密加盐',
  `state` int(11) DEFAULT NULL COMMENT '已注册 已激活 禁用',
  `age` int(2) DEFAULT NULL,
  `createtime` datetime DEFAULT NULL,
  `headImg` varchar(255) DEFAULT NULL,
  `logininfo_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

3.1 Employee表

CREATE TABLE `t_employee` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `phone` varchar(255) DEFAULT NULL,
  `salt` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `state` int(1) DEFAULT NULL,
  `department_id` bigint(20) DEFAULT NULL,
  `logininfo_id` bigint(20) DEFAULT NULL,
  `shop_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `FK4AFD4ACE851EFECF` (`department_id`)
) ENGINE=InnoDB AUTO_INCREMENT=388 DEFAULT CHARSET=utf8;

中间表 t_logininfo

CREATE TABLE `t_logininfo` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `phone` varchar(255) DEFAULT NULL,
  `email` varchar(255) DEFAULT NULL,
  `salt` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `type` int(22) DEFAULT NULL COMMENT '0 代表管理员 1 用户',
  `disable` int(22) DEFAULT NULL COMMENT '0 不可以用 1 可用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;

用到的工具类  1    AjaxResult   返回给前端, resultObj可携带对象集合给到前端展示

package cn.wl.basic.util;
import lombok.Data;


@Data
public class AjaxResult {
    private Boolean success = true;
    private String message = "操作成功";
    private Object resultObj;

    public static AjaxResult me(){
        return new AjaxResult();
    }

    public AjaxResult setResultObj(Object resultObj) {
        this.resultObj=resultObj;
        return this;
    }

    //失败构造
    public static AjaxResult fail(String message){
        return me().setSuccess(false).setMessage(message);
    }
    //链式编程
    public AjaxResult setSuccess(Boolean success) {
        this.success = success;
        return this;
    }
    //链式编程
    public AjaxResult setMessage(String message) {
        this.success = false;
        this.message = message;
        return this;
    }


}

 工具类2  MD5Utils  作用对密码加密

package cn.wl.basic.util;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class MD5Utils {

    /**
     * 加密
     * @param context
     */
    public static String encrypByMd5(String context) {
        try {  
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(context.getBytes());//update处理  
            byte [] encryContext = md.digest();//调用该方法完成计算  
  
            int i;  
            StringBuffer buf = new StringBuffer("");  
            for (int offset = 0; offset < encryContext.length; offset++) {//做相应的转化(十六进制)  
                i = encryContext[offset];  
                if (i < 0) i += 256;  
                if (i < 16) buf.append("0");  
                buf.append(Integer.toHexString(i));  
           }  
            return buf.toString();
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block  
            e.printStackTrace();
            return  null;
        }  
    }

    public static void main(String[] args) {

    //加密
    //1 生成随机盐值
    String pwd = "1";
    String salt = StrUtils.getComplexRandomString(32);
    //2 通过这个盐值加密
    String md5Pwd = MD5Utils.encrypByMd5(pwd +"yhp"+ salt+"xxxx");
    System.out.println(md5Pwd);


    //密码比对
    //1 查询盐值-就是salt
    String saltTmp = salt;
    //3 加密比对
    String pwdTmp = "1";
    String inputMd5Pwd = MD5Utils.encrypByMd5(pwdTmp +"yhp"+ saltTmp+"xxxx");
    if (inputMd5Pwd.equals(md5Pwd)){
        System.out.println("登录成功!");
    }else{
        System.out.println("密码错误");
    }
}


}

一:前端对接口传递的数据进行处理

  this.$http.post("/login/account",this.ruleForm2)//this.ruleForm2为表单内容
                    .then(result =>{
                        result=result.data;
                        if(result.success){
                            this.logining=false;
                            let{token,loginInfo}= result.resultObj;
                           localStorage.setItem("token",token);//后端传递的map中的数据
                           localStorage.setItem("loginInfo",JSON.stringify(loginInfo))
                            this.$router.push({ path: '/echarts' }); //跳转主页
                        }else {
                            this.logining = false;
                            this.$message({
                                message:result.message,
                                type:'error'
                            })
                        }
                    })

以下开始做拦截配置 。一共4个拦截器。目前是企业中公认配置最优化的拦截

1axios前置拦截

//--------第一个axios前置拦截器
axios.interceptors.request.use(config=>{
    let token = localStorage.getItem("token");
    if(token){
        config.headers['token']=token;//请求头携带token到后端
    }
    return config;
},error => {Promise.reject(error)});

 2 后台java代码配置

package cn.wl.basic.interceptor;

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;

@Configuration//声明是这是一个配置类
public class WebConfigurer implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //放行swagger访问的路径配置
        String[] swaggerExcludes = new String[]{"/swagger-ui.html","/swagger-resources/**","/webjars/**"};
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")//拦截所有的请求
                //不需要拦截的请求
                .excludePathPatterns("/user/register/**")
                .excludePathPatterns("/verifycode/**")
                .excludePathPatterns("/login/**")
                .excludePathPatterns("/fastDfs/**").excludePathPatterns(swaggerExcludes);
    }
}

1.1使用拦截配置类

package cn.wl.basic.interceptor;

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

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

/** 登录拦截与放行 */
@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        String token = request.getHeader("token");
        if (!StringUtils.isEmpty(token)){//不是空的,说明已经登录过了
            Object o = redisTemplate.opsForValue().get(token);
            if( o != null){
                redisTemplate.opsForValue().set(token,o,30, TimeUnit.MINUTES);//如果30分钟不操作。自动过期,需要重新登录
                return  true;
            }
        }
        //如果token为空的话,直接返回以下信息给到 第三个:axios的后置拦截器
        response.setContentType("application/json;charset=utf-8");
        response.setCharacterEncoding("UTF-8");
        PrintWriter writer = response.getWriter();
        writer.write("{\"success\":false,\"message\":\"noLogin\"}");
        writer.flush();
        writer.close();
        return false;//阻值放行
    }
}
3 axios的后置拦截器 处理后端第二个拦截器的报错信息

//第三个:axios的后置拦截器 处理后端第二个拦截器的报错信息
axios.interceptors.response.use(config=>{
    let data = config.data;
    if(!data.success && "noLogin"===data.message)
    {
        localStorage.removeItem("token");
        localStorage.removeItem("loginInfo");
        router.push({ path: '/login' });
    }
    return config;
},error => {
    Promise.reject(error)
})

4 前端静态资源拦截器也叫做路由拦截

//登录和注册在没有登录情况下也能访问
router.beforeEach((to, from, next) => {
    if (to.path == '/login'|| to.path=="/shopin") {
        next()
    }else{
        //否则需要判断是否已经登录
        let user = localStorage.getItem('loginInfo');
        if (!user) { //未登录
            next({ path: '/login' })
        } else {
            next() //登录了,正常执行
        }
    }
})

 一共4个拦截器。从axios前置拦截开始---后端 ---- axois后置拦截---静态资源也叫路由拦截

xios前置拦截:拦截请求发出来的请求。其实就是把请求头里面写上token信息,给后端拦截判断是否登录过

后端拦截:不拦截那些请求。放行那些请求。 1取出token 如果取不到,写data数据给到axios后置垃圾

axois后置拦截:处理后端传递来的信息

静态资源也叫路由拦截:如果直接访问公共的路由 放行资源

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值