Springboot 實現api校驗和登錄驗證


https://blog.csdn.net/qq_36085004/article/details/83348144 

 

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");
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM