springboot整合JWT实现鉴权实现栗子控制访问权限和按钮操作权限


一、JWT是什么?

JWT全称是:json web token。它将用户信息加密到 token 里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证 token 的正确性,只要正确即通过验证。
优点

1.简介:可以通过 URL POST 参数或者在 HTTP header 发送,因为数据量小,传输速度也很快;

2.自包含:负载中可以包含用户所需要的信息,避免了多次查询数据库;

3.因为 Token 是以 JSON 加密的形式保存在客户端的,所以 JWT 是跨语言的,原则上任何 web 形式都支持;

4.不需要再服务端保存会话信息,特别适用于分布式微服务;

缺点

1.无法作废已经发布的令牌;

2.不易应对数据过期;

二、集成JWT实例

1.引入依赖

    <dependencies>

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.34</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus</artifactId>
            <version>3.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- alibaba durid 数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.72</version>
        </dependency>
        <!-- JWT 鉴权依赖 -->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.8.1</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>5.4.6</version>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.15</version>
        </dependency>

    </dependencies>

2.编写认证工具类jwtUtil

代码如下:

package com.demo.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.sbm.productionmodelmap.Exception.JwtException;

import java.util.Date;

/**
 * <p>Description:
 * Jwt 认证授权方式用于生成签名校验和通过签名获取用户信息
 *
 * </p>
 *
 * @author Editor MartinZac
 * @date 2021年08月03日 9:27
 */
public class JwtUtils {

    /**
     * 设置过期时间24小时
     */
    private static final long EXPIRE_TIME =24 * 60 * 60 * 1000;

    /**
     * jwt 秘钥
     */
    private static final String SECRET = "jwt_secret";

    /**
     * 生成token签名设置过期时间
     *
     * @param userId 用户id
     * @return
     */
    public static String sign(String userId) {
        try {
            Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            return JWT.create()
                    //将userid 保存到token
                    .withAudience(userId)
                    //设置token过期时间
                    .withExpiresAt(date)
                    //token秘钥
                    .sign(algorithm);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 根据token 秘钥解密取出userID
     * @param token
     * @return
     */
    public static String getUserId(String token) {
        try {
            //从JWT获取userID
            String userId = JWT.decode(token).getAudience().get(0);
            return userId;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 校验token
     * @param token
     * @return
     */
    public static boolean checkSign(String token){
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm)
                    .build();
            DecodedJWT jwt = verifier.verify(token);
            return true;
        }catch (JWTVerificationException exception){
            throw new JwtException(401,"token 无效,请重新获取");
        }
    }
}

3.自定义注解@JwtAuth

代码如下:

package com.demo.target;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 定义需要登录才能访问的接口
 * jwt 
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface JwtAuth{
    boolean required() default true;
}

4.编写拦截器拦截声明的@JwtAuth注解接口

代码如下:

package com.demo.interceptor;

import com.sbm.productionmodelmap.Exception.JwtException;
import com.sbm.productionmodelmap.entity.SysUser;
import com.sbm.productionmodelmap.service.UserService;
import com.sbm.productionmodelmap.target.AppPermissions;
import com.sbm.productionmodelmap.target.JwtToken;
import com.sbm.productionmodelmap.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * <p>Description:
 * jwt 拦截器类拦截所有带注解请求
 * </p>
 *
 * @author Editor MartinZac
 * @date 2021年08月03日 10:41
 */
@Slf4j
public class JwtInterceptor implements HandlerInterceptor {

    @Resource
    private UserService userService;

    /**
     * 在请求前执行验证
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //解决跨域
        if("OPTIONS".equals(request.getMethod().toUpperCase())) {
            return true;
        }

        //从http 请求头取出token
        String token = request.getHeader("token");
        //校验不是映射方法通过请求
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();

        //定义userId
        String userId = "";

        if (method.isAnnotationPresent(JwtToken.class)) {
            JwtToken jwtToken = method.getAnnotation(JwtToken.class);
            if (jwtToken.required()) {
                //执行认证
                if (token == null) {
                    throw new JwtException(401, "您还没有授权请先登录");
                }

                //获取token中的 userId
                 userId = JwtUtils.getUserId(token);
                log.info("当前token:-->:" + token + "<----->userID为:" + userId);

                //验证token
                JwtUtils.checkSign(token);
            }
        }
       
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

5.注册拦截器

代码如下:

package com.demo.config;

import com.sbm.productionmodelmap.interceptor.JwtInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * <p>Description:
 * 注册拦截器类将jwt 拦截器注入
 * </p>
 *
 * @author Editor MartinZac
 * @date 2021年08月03日 11:15
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {
    /**
     * 注册拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor())
                .addPathPatterns("/**");
    }
    @Bean
    public JwtInterceptor jwtInterceptor() {
        return new JwtInterceptor();
    }
}

6.全局异常处理

代码如下:

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ResponseBody
    @ExceptionHandler(Exception.class)
    public Object handleException(Exception e) {
        String msg = e.getMessage();
        if (msg == null || msg.equals("")) {
            msg = "服务器出错";
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("message", msg);
        return jsonObject;
    }
}

7.编写接口

代码如下:

   /**
     * 登录并获取token
     * @param userName
     * @param passWord
     * @return
     */
    @PostMapping("/login")
    public Object login( String userName, String passWord){
        JSONObject jsonObject=new JSONObject();
        // 检验用户是否存在(为了简单,这里假设用户存在,并制造一个uuid假设为用户id)
        String userId = UUID.randomUUID().toString();
        // 生成签名
        String token= JwtUtil.sign(userId);
        Map<String, String> userInfo = new HashMap<>();
        userInfo.put("userId", userId);
        userInfo.put("userName", userName);
        userInfo.put("passWord", passWord);
        jsonObject.put("token", token);
        jsonObject.put("user", userInfo);
        return jsonObject;
    }

    /**
     * 该接口需要带签名才能访问
     * @return
     */
    @JwtToken
    @GetMapping("/getMessage")
    public String getMessage(){
        return "你已通过验证";
    }
}

8.测试,先访问登录接口,获取返回中的token,在将token加到jwt/getMessage接口请求头即可正常访问

原理即使用拦截器拦截请求获取请求头进行校验


总结

当前为springboot整合JWT的实例,还可以根据项目复杂度通过自定义注解的方式进行权限校验,拦截请求校验权限参数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值