當看到這個標題時,突然想到之前找工作時,室友被面試官問有沒有自定義過注解,室友蒙了,回來告訴我們,結果我們一圈蒙了......
今天看到這個題目,不得不來補一下之前的舊賬了,萬一以后面試再被cue呢?
好了,話不多說。
今天我們實現的自定義注解是一個Token驗證。
一共分如下幾步:
1.定義Token的注解,需要Token校驗的接口,方法上加上此注解;
1 import java.lang.annotation.ElementType; 2 import java.lang.annotation.Retention; 3 import java.lang.annotation.RetentionPolicy; 4 import java.lang.annotation.Target; 5 @Retention(RetentionPolicy.RUNTIME) 6 @Target(ElementType.METHOD) 7 public @interface Token { 8 boolean validate() default true; 9 }
2.定義LoginUser注解,此注解加在參數上,用在需要從token里獲取的用戶信息的地方;
1 import java.lang.annotation.ElementType; 2 import java.lang.annotation.Retention; 3 import java.lang.annotation.RetentionPolicy; 4 import java.lang.annotation.Target; 5 @Target(ElementType.PARAMETER) 6 @Retention(RetentionPolicy.RUNTIME) 7 public @interface LoginUser { 8 }
3.權限的校驗攔截器;
1 import com.example.demo.annotation.Token; 2 import com.example.demo.entity.User; 3 import lombok.extern.slf4j.Slf4j; 4 import org.springframework.stereotype.Component; 5 import org.springframework.web.method.HandlerMethod; 6 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 7 import javax.servlet.http.HttpServletRequest; 8 import javax.servlet.http.HttpServletResponse; 9 @Component 10 @Slf4j 11 public class AuthorizationInterceptor extends HandlerInterceptorAdapter { 12 public static final String USER_KEY = "USER_ID"; 13 public static final String USER_INFO = "USER_INFO"; 14 @Override 15 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 16 Token annotation; 17 if(handler instanceof HandlerMethod) { 18 annotation = ((HandlerMethod) handler).getMethodAnnotation(Token.class); 19 }else{ 20 return true; 21 } 22 //沒有聲明需要權限,或者聲明不驗證權限 23 if(annotation == null || annotation.validate() == false){ 24 return true; 25 } 26 //從header中獲取token 27 String token = request.getHeader("token"); 28 if(token == null){ 29 log.info("缺少token,拒絕訪問"); 30 return false; 31 } 32 //查詢token信息 33 // User user = redisUtils.get(USER_INFO+token,User.class); 34 // if(user == null){ 35 // log.info("token不正確,拒絕訪問"); 36 // return false; 37 // } 38 //token校驗通過,將用戶信息放在request中,供需要用user信息的接口里從token取數據 39 request.setAttribute(USER_KEY, "123456"); 40 User user=new User(); 41 user.setId(10000L); 42 user.setUserName("2118724165@qq.com"); 43 user.setPhoneNumber("15702911111"); 44 user.setToken(token); 45 request.setAttribute(USER_INFO, user); 46 return true; 47 } 48 }
4.寫參數的解析器,將登陸用戶對象注入到接口里;
1 import com.example.demo.annotation.LoginUser; 2 import com.example.demo.entity.User; 3 import com.example.demo.interceptor.AuthorizationInterceptor; 4 import org.springframework.core.MethodParameter; 5 import org.springframework.stereotype.Component; 6 import org.springframework.web.bind.support.WebDataBinderFactory; 7 import org.springframework.web.context.request.NativeWebRequest; 8 import org.springframework.web.context.request.RequestAttributes; 9 import org.springframework.web.method.support.HandlerMethodArgumentResolver; 10 import org.springframework.web.method.support.ModelAndViewContainer; 11 @Component 12 public class LoginUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver 13 { 14 @Override 15 public boolean supportsParameter(MethodParameter methodParameter) { 16 return methodParameter.getParameterType().isAssignableFrom(User.class)&&methodParameter.hasParameterAnnotation(LoginUser.class); 17 } 18 @Override 19 public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception { 20 //獲取登陸用戶信息 21 Object object = nativeWebRequest.getAttribute(AuthorizationInterceptor.USER_INFO, RequestAttributes.SCOPE_REQUEST); 22 if(object == null){ 23 return null; 24 } 25 return (User)object; 26 } 27 }
5.配置攔截器和參數解析器;
1 import com.example.demo.interceptor.AuthorizationInterceptor; 2 import com.example.demo.resolver.LoginUserHandlerMethodArgumentResolver; 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.context.annotation.Configuration; 5 import org.springframework.web.method.support.HandlerMethodArgumentResolver; 6 import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 7 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 8 import java.util.List; 9 10 @Configuration 11 public class WebMvcConfig implements WebMvcConfigurer { 12 @Autowired 13 private AuthorizationInterceptor authorizationInterceptor; 14 @Autowired 15 private LoginUserHandlerMethodArgumentResolver loginUserHandlerMethodArgumentResolver; 16 17 @Override 18 public void addInterceptors(InterceptorRegistry registry) { 19 registry.addInterceptor(authorizationInterceptor).addPathPatterns("/api/**"); 20 } 21 22 @Override 23 public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { 24 argumentResolvers.add(loginUserHandlerMethodArgumentResolver); 25 } 26 }
6.測試類;
1 import com.example.demo.annotation.LoginUser; 2 import com.example.demo.annotation.Token; 3 import com.example.demo.entity.User; 4 import lombok.extern.slf4j.Slf4j; 5 import org.springframework.web.bind.annotation.RequestMapping; 6 import org.springframework.web.bind.annotation.RequestMethod; 7 import org.springframework.web.bind.annotation.RestController; 8 9 @RestController 10 @RequestMapping(value = "/api") 11 @Slf4j 12 public class TestController { 13 @RequestMapping(value="/test",method = RequestMethod.POST) 14 @Token 15 public String test(@LoginUser User user){ 16 System.out.println("需要token才可以訪問,呵呵……"); 17 log.info("user:"+user.toString()); 18 return "test"; 19 } 20 @RequestMapping(value="/noToken",method = RequestMethod.POST) 21 public String noToken(){ 22 System.out.println("不用token就可以訪問……"); 23 return "test"; 24 } 25 }
至此,自定義注解實現token校驗就大功告成了。
參考及致謝:
Over.......