Springboot 实现api校验和登录验证

API校验

场景

在前后端分离开发时,后端获取数据就是通过异步请求调我们的API接口,但是,如果我们不做安全处理,其他人也可以直接调我们的API,这会让我们的数据泄露。因此,为了让我们的API只能被我们允许的人调用,我们对我们的API进行安全处理,他人在调用我们的API时需要进行校验,符合的才允许调用API。

实现思路

客户端:
调用我们API的人需要用时间戳timestamp,随机字符串noncestr,请求参数以升序排列拼接成一个字符串,并使用MD5进行加密生成一个签名sign。
在发送请求时,将timestamp, noncestr,sign发送给后台

后台:
编写一个拦截器,将所有的请求拦截。
在拦截器中进行请求校验:
1,请求参数sign是否为空,为空返回false。
2,timestamp 加十分钟(超过10分钟请求过期)是否小于服务端当前时间戳,小于返回false。
3,后台获取所有参数,以同样的规则拼接字符串,使用MD5加密,得到一个签名,用得到的签名和请求传来的签名进行比较,相同则放行,不同返回false。

代码
拦截器:
package com.xyy.edlp.intercepter;

import org.springframework.util.DigestUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * @Author: perkins
 */
public class ApiSignatureInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        Enumeration<String> paramNames = request.getParameterNames();
        String timestamp = request.getHeader("timestamp");

        long timestampDate = Long.valueOf(timestamp) + 1000*60*10;
        long currDate = System.currentTimeMillis();
        // 请求过期
        if (timestampDate < currDate) {
            response.setStatus(403);
            return false;
        }

        String noncestr = request.getHeader("noncestr");
        String signature = request.getParameter("sign");
        System.out.println(signature);

        if (signature == null) {
            response.setStatus(403);
            return false;
        }
        Map map = new HashMap();

        //获取所有的请求参数
        while (paramNames.hasMoreElements()) {
            String paramName = paramNames.nextElement();
            String[] paramValues = request.getParameterValues(paramName);

            if (paramValues.length > 0) {
                String paramValue = paramValues[0];
                System.out.println(paramName);
                if (paramValue.length() != 0 && !"sign".equals(paramName)) {
                    map.put(paramName, paramValue);
                }
            }
        }

        Set setKey = map.keySet();
        Object[] keys = setKey.toArray();
        // 将请求参数升序排序
        Arrays.sort(keys);

        StringBuilder strBuilder = new StringBuilder();
        for (Object str : keys) {
            strBuilder.append(str.toString());
            strBuilder.append(map.get(str.toString()));
        }

        strBuilder.append("noncestr");
        strBuilder.append(noncestr);
        strBuilder.append("timestamp");
        strBuilder.append(timestamp);

        System.out.println(strBuilder.toString());
        String newSignature = DigestUtils.md5DigestAsHex(strBuilder.toString().getBytes()).toUpperCase();

        if (!signature.equals(newSignature)) {
            response.setStatus(403);
            return false;
        }
        return true;
    }
}

拦截器注册:
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(new ApiSignatureInterceptor());
    }
}

登录token权限验证

场景

系统中,有的api必须用户登陆了才能够调用,因此,必须给这样的api进行安全防护。

实现思路

1,客户端调用登录接口,登录成功,使用JWT生成一个token,将token以UID—token键值对的形式存入redis,返回给客户端一个token和UID。
2,创建一个拦截器,对需要登录权限的接口进行拦截,判断请求中是否有token,根据UID从redis中取出对应的token,对请求中的token进行验证,然后再使用JWT验证token,都没问题放行,否则返回false。

代码
jwt生成token代码
package com.xyy.edlp.util;


import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.io.UnsupportedEncodingException;
import java.util.Date;

/**
 * @Author: perkins
 */
public class JwtUtil {
    private static final String encodeSecretKey = "XX#$%()(#*!()!KL<><MQLMNQNQJQKsdfkjsdrow32234545fdf>?N<:{LWPW";

    /**
     * token过期时间
     */
    private static final long EXPIRE_TIME = 1000 * 60 * 60 * 24 * 7;

    /**
     * 生成token
     * @return
     */
    public static String createToken(String account) {
        try {
            Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
            Algorithm algorithm = Algorithm.HMAC256(account + encodeSecretKey);
            return JWT.create()
                    .withExpiresAt(date)
                    .withClaim("account", account)
                    .sign(algorithm);
        } catch (UnsupportedEncodingException e) {
            return null;
        }
    }

    /**
     * 校验token是否失效
     * @param token
     * @return
     */
    public static boolean checkToken(String token, String account) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(account + encodeSecretKey);
            JWTVerifier verifier = JWT.require(algorithm)
                    .build();
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (UnsupportedEncodingException e) {
            return false;
        }
    }

    /**
     * 获取用户account
     * @param token
     * @return
     */
    public static String getAccount(String token){
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("account").asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    }

拦截器代码:
public class JwtInterceptor extends HandlerInterceptorAdapter {

    @Autowired
    RedisUtil redisUtil;

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        String token = request.getHeader("Authorization");
        if (token == null) {
            response.setStatus(401);
            return false;
        }

        String account = JwtUtil.getAccount(token);
        String redisToken = redisUtil.get(RedisKey.TP_STORE_KEY + account);
        boolean isExpire = JwtUtil.checkToken(token, account);

        if (redisToken == null || redisToken != token || isExpire) {
            response.setStatus(401);
            return false;
        }
        return true;
    }
}
拦截器注册:
@Configuration
public class WebConfig implements WebMvcConfigurer {

   // 再拦截器中使用了RedisUtil bean类,但是拦截器执行实在spring容器bean初始化之前的
   // RedisUtil 将无法注入,为了解决该问题,将JwtInterceptor拦截器先配置为一个bean
   // 在注册拦截器时,直接使用配置的bean
    @Bean
    public JwtInterceptor jwtInterceptor(){
        return new JwtInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(jwtInterceptor())
                .addPathPatterns("/tp_store/logout");
    }
}
  • 6
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值