Skip to content

Commit

Permalink
Merge pull request #5 from addw1/ning/summer-code
Browse files Browse the repository at this point in the history
Added the token interceptor for the http req
  • Loading branch information
xiaoshuwei authored Aug 16, 2024
2 parents 5ad6513 + 7064ec1 commit 02482b5
Show file tree
Hide file tree
Showing 12 changed files with 346 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.ning.codebot.common.common.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

/**
* distributed lock
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RedissonLock {

String prefixKey() default "";
String key();
/**
* do not wait -1
* @return second
*/
int waitTime() default -1;
TimeUnit unit() default TimeUnit.MILLISECONDS;

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.ning.codebot.common.common.aspect;


import cn.hutool.core.util.StrUtil;
import com.ning.codebot.common.common.annotation.RedissonLock;
import com.ning.codebot.common.common.service.LockService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.ning.codebot.common.common.utils.SpElUtils;

import java.lang.reflect.Method;

@Slf4j
@Aspect
@Component
@Order(0)
public class RedissonLockAspect {
@Autowired
private LockService lockService;

@Around("@annotation(com.ning.codebot.common.common.annotation.RedissonLock)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
RedissonLock redissonLock = method.getAnnotation(RedissonLock.class);
String prefix = StrUtil.isBlank(redissonLock.prefixKey()) ? SpElUtils.getMethodKey(method) : redissonLock.prefixKey();//默认方法限定名+注解排名(可能多个)
String key = SpElUtils.parseSpEl(method, joinPoint.getArgs(), redissonLock.key());
return lockService.executeWithLockThrows(prefix + ":" + key, redissonLock.waitTime(), redissonLock.unit(), joinPoint::proceed);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.ning.codebot.common.common.config;

import com.ning.codebot.common.common.interceptor.TokenInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
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 {

@Autowired
private TokenInterceptor tokenInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor)
.addPathPatterns("/codebot/**");

}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.ning.codebot.common.common.exception;

public interface ErrorEnum {

Integer getErrorCode();

String getErrorMsg();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.ning.codebot.common.common.exception;

import cn.hutool.http.ContentType;
import cn.hutool.json.JSONUtil;
import com.google.common.base.Charsets;
import com.ning.codebot.common.domain.vo.response.ApiResult;
import lombok.AllArgsConstructor;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@AllArgsConstructor
public enum HttpErrorEnum implements ErrorEnum{
ACCESS_DENIED(401, "token is invalid, please login"),
;
private Integer httpCode;
private String msg;

@Override
public Integer getErrorCode() {
return httpCode;
}

@Override
public String getErrorMsg() {
return msg;
}

public void sendHttpError(HttpServletResponse response) throws IOException {
response.setStatus(this.getErrorCode());
ApiResult responseData = ApiResult.fail(this);
response.setContentType(ContentType.JSON.toString(Charsets.UTF_8));
response.getWriter().write(JSONUtil.toJsonStr(responseData));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.ning.codebot.common.common.interceptor;

import com.ning.codebot.common.common.exception.HttpErrorEnum;
import com.ning.codebot.common.user.service.LoginService;
import lombok.extern.slf4j.Slf4j;
import org.jboss.logging.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;
import java.util.Optional;

@Order(-2)
@Slf4j
@Component
public class TokenInterceptor implements HandlerInterceptor {
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String AUTHORIZATION_SCHEMA = "Bearer ";
public static final String ATTRIBUTE_UID = "uid";

@Autowired
private LoginService loginService;


@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//get login user token
String token = getToken(request);
Long validUid = loginService.getValidUid(token);
if (Objects.nonNull(validUid)) {
request.setAttribute(ATTRIBUTE_UID, validUid);
} else {
boolean isPublicURI = isPublicURI(request.getRequestURI());
if (!isPublicURI) {
HttpErrorEnum.ACCESS_DENIED.sendHttpError(response);
return false;
}
}
return true;
}

/**
* Check whether it is public resources
* @param requestURI
*/
private boolean isPublicURI(String requestURI) {
//TODO: add check logic
return false;
}
private String getToken(HttpServletRequest request) {
String header = request.getHeader(AUTHORIZATION_HEADER);
return Optional.ofNullable(header)
.filter(h -> h.startsWith(AUTHORIZATION_SCHEMA))
.map(h -> h.substring(AUTHORIZATION_SCHEMA.length()))
.orElse(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.ning.codebot.common.common.service;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import lombok.SneakyThrows;

import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

@Service
@Slf4j
public class LockService {

@Autowired
private RedissonClient redissonClient;

public <T> T executeWithLockThrows(String key, int waitTime, TimeUnit unit, SupplierThrow<T> supplier) throws Throwable {
RLock lock = redissonClient.getLock(key);
boolean lockSuccess = lock.tryLock(waitTime, unit);
if (!lockSuccess) {
//TODO: add new exception
throw new Exception();
}
try {
return supplier.get();
} finally {
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}

@SneakyThrows
public <T> T executeWithLock(String key, int waitTime, TimeUnit unit, Supplier<T> supplier) {
return executeWithLockThrows(key, waitTime, unit, supplier::get);
}

public <T> T executeWithLock(String key, Supplier<T> supplier) {
return executeWithLock(key, -1, TimeUnit.MILLISECONDS, supplier);
}

@FunctionalInterface
public interface SupplierThrow<T> {

/**
* Gets a result.
*
* @return a result
*/
T get() throws Throwable;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.ning.codebot.common.common.utils;

import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.util.Optional;

public class SpElUtils {
private static final ExpressionParser parser = new SpelExpressionParser();
private static final DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

public static String parseSpEl(Method method, Object[] args, String spEl) {
String[] params = Optional.ofNullable(parameterNameDiscoverer.getParameterNames(method)).orElse(new String[]{});//解析参数名
EvaluationContext context = new StandardEvaluationContext();
for (int i = 0; i < params.length; i++) {
context.setVariable(params[i], args[i]);
}
Expression expression = parser.parseExpression(spEl);
return expression.getValue(context, String.class);
}

public static String getMethodKey(Method method) {
return method.getDeclaringClass() + "#" + method.getName();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.ning.codebot.common.domain.vo.response;


import com.ning.codebot.common.common.exception.ErrorEnum;
import lombok.Data;

@Data
public class ApiResult<T> {

private Boolean success;

private Integer errCode;

private String errMsg;

private T data;

public static <T> ApiResult<T> success() {
ApiResult<T> result = new ApiResult<T>();
result.setData(null);
result.setSuccess(Boolean.TRUE);
return result;
}

public static <T> ApiResult<T> success(T data) {
ApiResult<T> result = new ApiResult<T>();
result.setData(data);
result.setSuccess(Boolean.TRUE);
return result;
}

public static <T> ApiResult<T> fail(Integer code, String msg) {
ApiResult<T> result = new ApiResult<T>();
result.setSuccess(Boolean.FALSE);
result.setErrCode(code);
result.setErrMsg(msg);
return result;
}

public static <T> ApiResult<T> fail(ErrorEnum errorEnum) {
ApiResult<T> result = new ApiResult<T>();
result.setSuccess(Boolean.FALSE);
result.setErrCode(errorEnum.getErrorCode());
result.setErrMsg(errorEnum.getErrorMsg());
return result;
}

public boolean isSuccess() {
return this.success;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.ning.codebot.common.repository.controller;

import com.ning.codebot.common.common.annotation.RedissonLock;
import com.ning.codebot.common.domain.vo.response.ApiResult;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("codebot/reporsitory")
public class RepoController {
@PostMapping("/subscribe")
@RedissonLock(key = "#subscribe")
public ApiResult<Void> subscribe(@RequestBody String author, @RequestBody String repoName){
//TODO: finish the check logic to avoid multiple times download
return new ApiResult<>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public String callback(@RequestParam("code") String code, @RequestParam("state")
// token -> userInfo
String userInfo = getUserInfo(accessToken);
outh2Service.storeUserInfo(Integer.parseInt(state), new GithubUserInfo(JsonUtils.toJsonNode(userInfo)));
log.info("redirect to the home page");
// log.info("redirect to the home page");
return "redirect:/test";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@


@Controller
@RequestMapping("/user")
@RequestMapping("codebot/user")
public class UserController {

}
Expand Down

0 comments on commit 02482b5

Please sign in to comment.