前后端分离实现用户登录Token权限验证

Springboot+Vue实现用户登录Token权限验证

1.前后端分离实现token登录验证和状态保存

token可用于登录验证和权限管理。

大致步骤分为:

- 前端登录,post用户名和密码到后端。

- 后端验证用户名和密码,若通过,生成一个token返回给前端。

- 前端拿到token用vuex和localStorage管理,登录成功进入首页。

- 之后前端每一次权限操作如跳转路由,都需要判断是否存在token,若不存在,跳转至登录页。

- 前端之后的每一个对后端的请求都要在请求头上带上token,后端查看请求头是否有token,拿到token检查是否过期,返回对应状态给前端。

- 若token已过期,清除token信息,跳转至登录页。

前端功能设计:

​ (1)main.js主文件:添加路由前置守卫beforeEach拦截请求

//路由全局前置守卫
router.beforeEach((to,from,next) => {
  if(to.path === '/register' || to.path === '/login' || to.path === '/'){ //若是进入登录与注册页面 ==> pass
    next()
  }else{ 
    let userToken = localStorage.getItem('token');
    console.log("Token为:"+userToken); 
    if(userToken == null || userToken == ''){
      alert("无权限,请先登录!");
      return next('/login');
    }else{
      next();
    }
  }
}),

​ (2)Login页面:发送用户名与密码;接收后端生成的token并存入状态管理中localStorage

if(res.status == 1){
          localStorage.setItem("username",this.form.username); //将用户名存入缓存,TheHeader 用户名直接从缓存中取出
          this.userToken = res.token;
           // 将用户token保存到localStorage中  备注:先将token存入状态管理中,登陆成功跳转到首页要用到token进行验证
          this.CHANGE_LOGIN({ token : this.userToken });
          this.$router.push('/Info');
          this.$message({
            message: '登录成功',
            type: 'success'
          })

(3)在store状态管理中存储token:

const store = new VueX.Store({
    state: {
        HOST: `http://127.0.0.1:8888`,  // //歌手图片地址加载 ,因为库中存的是相对地址  如果不加http:// ,URL前面就会携带上vue的本地启动ip
        
        // 存储token
        Authorization: localStorage.getItem('token') ? localStorage.getItem('token') : ''
    },
    // 修改token,并将token存入localStorage
    export const CHANGE_LOGIN = (state, user) => {
        state.token = user.token;
        localStorage.setItem('token', user.token);
      };
 
})

(4) 在axios中添加请求拦截器:

//请求拦截器 在请求头中加token
axios.interceptors.request.use(
    config => {
        if(localStorage.getItem('token')){
            config.headers.token = localStorage.getItem('token');
        }
        return config;
    },
    error => {
        return Promise.reject(error);
    }
)

后端功能设计:

​ pom.xml引入依赖:

<dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.18.2</version>
        </dependency>

​ LoginController(登录调用方法生成token签名):

   		Admin admin = new Admin();
           admin.setName(name);
           admin.setPassword(password);
           String signToken = TokenUtil.sign(admin);//首次登录token签名
           jsonObject.put("token",signToken);
           return jsonObject;

​ TokenUtil(生成token与验证):

package com.haiyang.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.haiyang.domain.Admin;
import java.util.Date;

public class TokenUtil {

    private static final long EXPIRE_TIME= 10*60*60*1000; //过期时间:10 hour
    private static final String TOKEN_SECRET="Haiyang";  //密钥盐

    /**
     * 签名生成
     * @param admin
     * @return
     */
    public static String sign(Admin admin){
        String token = null;
        try {
            Date expiresAt = new Date(System.currentTimeMillis() + EXPIRE_TIME);
            token = JWT.create()
                    .withIssuer("auth0")
                    .withClaim("username", admin.getName())
                    .withExpiresAt(expiresAt)
                    // 使用了HMAC256加密算法。
                    .sign(Algorithm.HMAC256(TOKEN_SECRET));
        } catch (Exception e){
            e.printStackTrace();
        }
        System.out.println(admin.getName()+"用户生成的Token为:"+token);
        return token;
    }

    /**
     * 签名验证
     * @param token
     * @return
     */
    public static boolean verify(String token){
        try {
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).withIssuer("auth0").build();
            DecodedJWT jwt = verifier.verify(token);
            System.out.println("认证通过:");
            System.out.println("username: " + jwt.getClaim("username").asString());
            System.out.println("过期时间:      " + jwt.getExpiresAt());
            return true;
        } catch (Exception e){
            return false;
        }
    }

}

​ TokenInterceptor(拦截token):

package com.haiyang.Interceptor;


import com.alibaba.fastjson.JSONObject;
import com.haiyang.utils.TokenUtil;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @version 1.0
 * @author:Haiyang
 * @date:2021/9/28
 */
@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"); //前端vue将token添加在请求头中
        if(token != null){
            boolean result = TokenUtil.verify(token);
            if(result){
                System.out.println("通过拦截器");
                return true;
            }
        }
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        try{
            JSONObject json = new JSONObject();
            json.put("msg","token verify fail");
            json.put("code","50000");
            response.getWriter().append(json.toJSONString());
            System.out.println("认证失败,未通过拦截器");

        }catch (Exception e){
            e.printStackTrace();
            response.sendError(500);
            return false;
        }
        return false;
    }
}

​ WebMVConfig配置:

package com.haiyang.config;

import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.alibaba.fastjson.support.spring.FastJsonJsonView;
import com.haiyang.Interceptor.TokenInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
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 com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    /**
     * 开启跨域
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 设置允许跨域的路由
        registry.addMapping("/**")
                // 设置允许跨域请求的域名
                .allowedOriginPatterns("*")
                // 是否允许携带cookie参数
                .allowCredentials(true)
                // 设置允许的方法
                .allowedMethods("*")
                // 跨域允许时间
                .maxAge(3600);
    }


    private TokenInterceptor tokenInterceptor;
    //构造方法
    public WebMvcConfig(TokenInterceptor tokenInterceptor){
        this.tokenInterceptor = tokenInterceptor;
    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer){
        configurer.setTaskExecutor(new ConcurrentTaskExecutor(Executors.newFixedThreadPool(3)));
        configurer.setDefaultTimeout(30000);
    }

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

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

}

  • 15
    点赞
  • 108
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
前后端分离是一种开发架构,将前端和后端的开发过程分开进行,前端主要负责用户界面展示和交互逻辑,后端负责数据处理和业务逻辑。而sa-token是一种单点登录的解决方案。 sa-token是基于Token的一个轻量级权限认证和申请令牌的工具,可以实现前后端分离项目的单点登录功能。它通过生成和验证Token令牌来实现用户身份认证。 在前后端分离的架构中,前端发送登录请求到后端,后端通过验证用户的账号和密码,如果验证成功,则生成一个Token令牌,并将该Token返回给前端。前端保存该Token每次向后端发送请求时,需要在请求的Header中添加Token。后端通过获取请求Header中的Token,并进行验证,如果验证通过,则表示用户已登录。 sa-token提供了一些便捷的API,用于生成Token验证Token、获取用户信息等操作。通过这些API,我们可以简便地实现单点登录的功能。sa-token还提供了一些定制化的配置选项,可以根据实际需求进行调整。 前后端分离的sa-token单点登录方案具有以下优点:简单易用、安全性高、扩展性强等。同时,由于前后端分离,使得前端和后端可以独立开发和部署,提高了开发效率和项目的可维护性。 总之,前后端分离sa-token单点登录是一种解决方案,通过Token令牌验证实现用户身份认证,适用于前后端分离的项目,具有诸多优点,可轻松实现单点登录功能。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值