token防禦CSRF攻擊
阿新 • • 發佈:2021-06-26
技術概述
- 為防止CSRF跨站點請求偽造,在請求地址中新增 token 並驗證。
技術詳述:
在pom.xml中新增依賴
<!--jwt-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
編寫工具類,利用JWT生成token
public class TokenUtil { public static String getToken(UserBO user) { return JWT.create().withAudience(String.valueOf(user.getId())) .sign(Algorithm.HMAC256(user.getOpenId())); } public static String getToken(AdminBO admin) { return JWT.create().withAudience(String.valueOf(admin.getId()+10000)) .sign(Algorithm.HMAC256(admin.getAccount())); } }
編寫兩個自定義註解
用來跳過驗證的PassToken
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken
{
boolean required() default true;
}
需要通過token認證
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface UserLoginToken { boolean required() default true; }
使用攔截器獲取token並進行驗證
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception
{
// 從 http 請求頭中取出 token
String token = httpServletRequest.getHeader("token");
// 如果不是對映到方法直接通過
if (!(object instanceof HandlerMethod))
{
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) object;
Method method = handlerMethod.getMethod();
//檢查是否有passtoken註釋,有則跳過認證
if (method.isAnnotationPresent(PassToken.class))
{
PassToken passToken = method.getAnnotation(PassToken.class);
if (passToken.required())
{
return true;
}
}
//檢查有沒有需要使用者許可權的註解
if (method.isAnnotationPresent(UserLoginToken.class))
{
UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
if (userLoginToken.required())
{
// 執行認證
if (token == null)
{
throw new RuntimeException("無token,請重新登入");
}
// 獲取 token 中的 user id
long id;
UserBO user=null;
AdminBO admin=null;
try
{
id = Long.parseLong(JWT.decode(token).getAudience().get(0));
user = userDAO.getUserById(id);
// 驗證 token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getOpenId())).build();
try
{
jwtVerifier.verify(token);
}
catch (JWTVerificationException e)
{
throw new RuntimeException("401");
}
}
catch (JWTDecodeException j)
{
throw new RuntimeException("401");
}
if (user == null)
{
throw new RuntimeException("使用者不存在,請重新登入");
}
return true;
}
}
return true;
}
配置攔截器
package com.example.fidledemo.config;
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;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer
{
@Override
public void addInterceptors(InterceptorRegistry registry)
{
registry.addInterceptor(authenticationInterceptor())
.addPathPatterns("/**");
}
@Bean
public AuthenticationInterceptor authenticationInterceptor()
{
return new AuthenticationInterceptor();
}
}
在控制器中處理使用者的登入請求時,生成token並返回。
String token=TokenUtil.getToken(userBO);
return JSON.toJSONString(Result.successResult(new LoginVO(personVO,token)));
流程圖
遇到的問題和解決過程
再此之前直接觸過CSRF和token驗證的理論,所以主要是對此應用不太熟練,後面也問了團隊的小夥伴還有在CSDN上查詢相關資料慢慢將這個功能完成了。
總結
我認為一個軟體其安全驗證是一個非常重要的部分,通過這次的實踐,我將之前學到的理論變成了現實,是自身的一種進步。