当看到这个标题时,突然想到之前找工作时,室友被面试官问有没有自定义过注解,室友蒙了,回来告诉我们,结果我们一圈蒙了......
今天看到这个题目,不得不来补一下之前的旧账了,万一以后面试再被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.......