我们平时在开发的时候,通常使用过滤器,拦截器,网关等等实现用户权限的校验、白名单的拦截等,今天我们在springcloud中使用自定义注解的方式来实现用户必须登录的情况下才能访问某个接口或者controller。
1.定义好注解:
package com.meiyibao.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 必须登录的注解,可以直接使用在方法上,方法上面使用以后,该方法必须有用户登录,否则直接会返回没有权限的异常
*
* @author root
*
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Target({ ElementType.TYPE, ElementType.METHOD }) // 1.接口、类、枚举、注解 2.方法
public @interface MustLogin {
public String value() default "";
}
2.定义拦截器(我这里包括了三个端的调用鉴权,有APP客户端,后台管理系统,和PC端的个人中心),另外,用户登录以后,需要把用户信息保存在redis中:
package com.meiyibao.interceptor;
import com.alibaba.fastjson.JSON;
import com.ctrip.framework.apollo.Config;
import com.meiyibao.annotation.Auth;
import com.meiyibao.annotation.MustLogin;
import com.meiyibao.annotation.RequireUser;
import com.meiyibao.annotation.WhiteIpAuth;
import com.meiyibao.bean.dto.SysUser;
import com.meiyibao.bean.dto.User;
import com.meiyibao.easycoal.framework.metadata.MybData;
import com.meiyibao.constants.MybConstants;
import com.meiyibao.redis.RedisUtil;
import com.meiyibao.util.ip.AgentUtil;
import com.meiyibao.util.json.JSONHelper;
import com.meiyibao.util.regex.ValidateHelper;
import org.springframework.web.method.HandlerMethod;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public interface BaseHandlerInterceptorAdapter {
default <T extends Annotation> T findAnnotation(HandlerMethod handler, Class<T> annotationType) {
T annotation = handler.getBeanType().getAnnotation(annotationType);
if (annotation != null)
return annotation;
return handler.getMethodAnnotation(annotationType);
}
/**
* 用户鉴权
*
* @param request
* @param response
* @param handler
* @return
*/
default boolean basePreHandle(HttpServletRequest request, HttpServletResponse response, Object handler, RedisUtil redis, String key, Config config) {
try {
if (handler instanceof HandlerMethod && handler != null) {
//必须登录
MustLogin mustLogin = findAnnotation((HandlerMethod) handler, MustLogin.class);
if (mustLogin != null) {
Object user = getLoginUser(request, redis, key);
if (user == null) {
responeWrite(response, JSONHelper.convertToJSON(MybData.error(MybConstants.LOGIN_EXCEPTION_CODE, MybConstants.EXCEPTION_MUST_LOGIN_ERROR)));
return false;
} else {
request.setAttribute(MybConstants.CURRENT_USER, user);
return true;
}
}
//获取用户信息封装到request中
RequireUser requireUser = findAnnotation((HandlerMethod) handler, RequireUser.class);
if (requireUser != null) {
Object user = getLoginUser(request, redis, key);
request.setAttribute(MybConstants.CURRENT_USER, user);
}
Auth auth = findAnnotation((HandlerMethod) handler, Auth.class);
// 新版用户权限要处理
if (auth != null) {
return false;
}
// IP白名单
WhiteIpAuth whiteIpAuth = findAnnotation((HandlerMethod) handler, WhiteIpAuth.class);
if (whiteIpAuth != null) {
String[] ipArray = config.getProperty("white.ip", "").split(",");
// ip白名单过滤
List<String> list = Arrays.asList(ipArray);
String ip = AgentUtil.getIpAddr(request);
if (ipArray == null || list.indexOf(ip) == -1) {
responeWrite(response, JSONHelper.convertToJSON(MybData.error(MybConstants.EXCEPTION_IP_AUTH_ERROR)));
return false;
}
}
}
return true;
} catch (Exception ex) {
return false;
}
}
/**
* 检查用户是否登录
*/
default Object getLoginUser(HttpServletRequest request, RedisUtil redis, String key) {
Object user = null;
String token = request.getHeader(MybConstants.AUTHORIZATION);
if (ValidateHelper.isNullOrEmpty(token)) {
token = request.getParameter(MybConstants.AUTHORIZATION);
}
if (ValidateHelper.isNullOrEmpty(token)) {
return user;
}
// 后台管理系统的鉴权
if (key.equals(MybConstants.TOKEN_ADMIN_USER_KEY)) {
String objStr = redis.getString(key + token);
if (objStr != null) {
user = JSON.parseObject(objStr, SysUser.class);
}
}
//APP系统的鉴权 PC个人中心的鉴权
else if (key.equals(MybConstants.TOKEN_APP_USER_KEY) || key.equals(MybConstants.TOKEN_WEB_USER_KEY)) {
List<String> keys = redis.scanMatch(key + "*" + token);
String objStr;
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()) {
objStr = redis.getString(iterator.next());
if (objStr != null) {
user = JSON.parseObject(objStr, User.class);
}
}
}
return user;
}
default void responeWrite(HttpServletResponse response, String content) throws IOException {
response.setContentType(MybConstants.JSON_FORMAT);
response.setStatus(MybConstants.HTTP_STATUS_SUCCESS_CODE);
response.getWriter().write(content);
}
}
3.实现BaseHandlerInterceptorAdapter接口进行调用,我这里是PC端服务端工程调用
package com.meiyibao.interceptor;
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfig;
import com.meiyibao.constants.MybConstants;
import com.meiyibao.redis.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 拦截器,用来拦截 后台管理系统或者 自定义的请求, 另外还用来处理回话过期的拦截和用户鉴权
*
* @author Ray
*/
@Component
public class AuthInterceptor extends HandlerInterceptorAdapter implements BaseHandlerInterceptorAdapter {
@Autowired
private RedisUtil redis;
@ApolloConfig
private Config config;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
return basePreHandle(request, response, handler, redis, MybConstants.TOKEN_ADMIN_USER_KEY, config);
}
}
4.配置拦截url,这里配置所有的url请求都必须要进入BaseHandlerInterceptorAdapter中:
package com.meiyibao.config;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.meiyibao.interceptor.AuthInterceptor;
import com.meiyibao.util.common.CustomFastJsonConfig;
import com.meiyibao.util.time.TimeHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 拦截器配置
*
*/
@Configuration
public class MvcConfig extends WebMvcConfigurationSupport {
@Autowired
private AuthInterceptor myInterceptor;
@Autowired
private RequestMappingHandlerAdapter handlerAdapter;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**");
super.addResourceHandlers(registry);
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter fastJsonHttpMessageConverter=new FastJsonHttpMessageConverter();
fastJsonHttpMessageConverter.setFastJsonConfig(CustomFastJsonConfig.FAST_JSON_CONFIG);
List<MediaType> mediaTypes=new ArrayList<>();
mediaTypes.add(MediaType.APPLICATION_JSON);
mediaTypes.add(new MediaType("application", "*+json"));
fastJsonHttpMessageConverter.setSupportedMediaTypes(mediaTypes);
converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
converters.add(fastJsonHttpMessageConverter);
}
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
}
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("GET");
corsConfiguration.addAllowedMethod("POST");
corsConfiguration.addAllowedMethod("OPTIONS");
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 4 对接口配置跨域设置
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor);
super.addInterceptors(registry);
}
@PostConstruct
public void initEditableValidation() {
ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer) handlerAdapter.getWebBindingInitializer();
if (initializer.getConversionService() != null) {
GenericConversionService genericConversionService = (GenericConversionService) initializer.getConversionService();
genericConversionService.addConverter(new Converter<String, Date>() {
@Override
public Date convert(String source) {
return TimeHelper.convert(source);
}
});
}
}
}
4.使用,在controller中,只需要加上mustLogin注解就可以简单越快的使用了
/**
* 订单历史方案列表
*
* @param po
* @return
*/
@MustLogin
@PostMapping("/admin/listOrderSearchSolutionHistory")
public Object listOrderSearchSolutionHistory(@RequestBody OrderSearchSolutionHistoryPo po) {
if (ValidateHelper.isNullOrEmpty(po.getOrderId())) {
return MybData.error("orderId不能为空");
}
return MybData.success(orderSearchSolutionHistoryService.listOrderSearchSolutionHistory(po));
}