文章目录
前言
项目中很多会用到获取用户基础信息,有时候我们会使用接口调用去数据库查询用户信息完成一系列操作,这样虽然也可以,但是很大一部分都是自己手动写好而且比较复杂化,所以JAVA有一个自定义注解,结合某些API的使用做一个标识提前封装好全局使用,只需要一个注解标识就可以获取用户一系列信息,是不是简单多了?OK(开怼)
核心依赖导入
<properties>
<spring.cloud-version>Finchley.SR4</spring.cloud-version>
<spring.cloud-other-version>2.0.4.RELEASE</spring.cloud-other-version>
<boot-version>2.0.4.RELEASE</boot-version>
<mysql-version>5.1.47</mysql-version>
<mybatis-plus-version>2.2.0</mybatis-plus-version>
<jwt-version>0.9.1</jwt-version>
</properties>
<dependencies>
<!--SpringBoot核心依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${boot-version}</version>
<exclusions><!-- 去掉默认配置 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${boot-version}</version>
</dependency>
<!--SpringBoot核心依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud-version}</version>
<!--TODO 必须添加如下设置才可导入Cloud-->
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
<version>${spring.cloud-other-version}</version>
</dependency>
<!--TODO Cloud自动化配置-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
<version>${spring.cloud-other-version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jwt-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
<!--maven编译器指定jdk版本-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
自定义注解新增
import java.lang.annotation.*;
/**
* @author xueWenLiang
* @date 2021/4/29 17:30
* @Description 描述信息
*/
@Documented
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestUser {
/**
* 是否查询LoginUser对象所有信息,true则通过rpc接口查询
*/
boolean value() default false;
}
自定义拦截注解配置类
import com.xwl.annotation.RequestUser;
import com.xwl.config.enums.XwlResultErrorEnum;
import com.xwl.config.exception.XwlException;
import com.xwl.domain.XwlUser;
import com.xwl.utils.JwtUtils;
import lombok.AllArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import javax.servlet.http.HttpServletRequest;
/**
* @author xueWenLiang
* @date 2021/4/30 10:10
* @Description 自定义注解控制器,解析token获取用户信息
*/
@AllArgsConstructor
@Component
public class XwlUserArgumentResolver implements HandlerMethodArgumentResolver {
/**
* 执行判断是否满足使用规范
* @param methodParameter
* @return true即跳出进行业务处理(resolveArgument)|| false不执行
*/
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
//是否是注解元素
boolean isTrueRequestUser = methodParameter.hasParameterAnnotation(RequestUser.class);
//是否注在指定用户信息对象
boolean isAnnotationXwlUser = methodParameter.getParameterType().isAssignableFrom(XwlUser.class);
return isTrueRequestUser==isAnnotationXwlUser;
}
/**
* 解析token封装用户信息
* @param methodParameter
* @param modelAndViewContainer
* @param nativeWebRequest
* @param webDataBinderFactory
* @return
* @throws Exception
*/
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
XwlUser xwlUser= null;
try {
xwlUser = JwtUtils.getUserParameters(request);
} catch (Exception e) {
throw new XwlException(XwlResultErrorEnum.FORBIDDEN);
}
//不排除以下情况->抛出无鉴权权限
if (ObjectUtils.isEmpty(xwlUser))throw new XwlException(XwlResultErrorEnum.FORBIDDEN);
return xwlUser;
}
}
自定义注解配置类装载器
/**
* @author xueWenLiang
* @date 2021/4/30 14:09
* @Description 描述信息
*/
@Configuration
@Component
@SuppressWarnings("all")
public class RequestMvcConfigutaionFilter implements WebMvcConfigurer {
/**
* 自定义注解控制器集合体处理
* @param resolvers
*/
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
//注入用户信息注解控制器
resolvers.add(new XwlUserArgumentResolver());
}
}
token工具类(生成,解析)
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import com.xwl.domain.XwlUser;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.*;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author xueWenLiang
* @date 2021/4/30 10:21
* @Description 描述信息
* iss: jwt签发者
*
* sub: jwt所面向的用户
*
* aud: 接收jwt的一方
*
* exp: jwt的过期时间,这个过期时间必须要大于签发时间
*
* nbf: 定义在什么时间之前,该jwt都是不可用的.
*
* iat: jwt的签发时间
*
* jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
*/
@Component
public class JwtUtils {
private static final String SIGN_NAME = "xuewenliang";
private static final String HEADER_TOKEN_KEY = "xwl_token";
private static long mill = 60000L;
private static final String ZHANG_SAN="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiLlvKDkuIkiLCJzdWIiOiIyNTAiLCJpYXQiOjE2MTk3NTM3MDF9.FyRuh7hvuB_mPDbCqCBGxB8YW4YDxj--XmVj2d-KzU4";
private static final String LI_SI="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiLmnY7lm5siLCJzdWIiOiLmuZbljZfnnIEiLCJpYXQiOjE2MjAyOTA2NDB9.iQa7ICboE6JHpJvvx8tFoOa-kWgw8Z0IYDeh8h0OXPo";
/**
* 生成token
*
* @param name
* @param emailAdress
* @return
*/
public static String createTokenByParams(String name, String emailAdress) {
//当前时间-毫秒
long now = System.currentTimeMillis();
JwtBuilder builder = Jwts.builder().setId(name)
.setSubject(emailAdress)
.signWith(SignatureAlgorithm.HS256, SIGN_NAME)
.setIssuedAt(new Date());
String compact = builder.compact();
return compact ;
}
/**
* 解析token
*
* @param token
* @return
*/
public static Claims parseClaimsByToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser().setSigningKey(SIGN_NAME).parseClaimsJws(token).getBody();
} catch (ExpiredJwtException e) {
System.out.println("token异常");
} catch (UnsupportedJwtException e) {
System.out.println("token异常");
} catch (MalformedJwtException e) {
System.out.println("token异常");
} catch (SignatureException e) {
System.out.println("token异常");
} catch (IllegalArgumentException e) {
System.out.println("无权限");
}
return claims;
}
/**
* 根据自定义注解注入分析请求头token封装用户信息返回
* @param request
* @return
*/
public static XwlUser getUserParameters(HttpServletRequest request) {
String tokenValue = request.getHeader(HEADER_TOKEN_KEY);
//这里不用判断是否为空
// TODO 备注:因为是否携带token由其它层负责拦截,用户信息获取token解析已经进入业务层
//解析token串获取信息
Claims claims = parseClaimsByToken(tokenValue);
XwlUser xwlUser = new XwlUser();
//设置名称
xwlUser.setUserName(claims.getId());
//设置地址
xwlUser.setUserAddress(claims.getSubject());
return xwlUser;
}
}
封装返回体工具类
import com.xwl.config.enums.XwlResultErrorEnum;
import lombok.Data;
/**
* @author xueWenLiang
* @date 2021/4/29 16:31
* @Description 描述信息
*/
@Data
public class R<T> {
private Integer code;
private String message;
private T data;
/**
* 无参成功返回体
*
* @param <T>
* @return
*/
public static <T> R<T> OK() {
return result(null, XwlResultErrorEnum.SUCCESS.getCode(), XwlResultErrorEnum.SUCCESS.getMsg());
}
/**
* 有数据返回体
*
* @param <T>
* @return
*/
public static <T> R<T> OK(T data) {
return result(data, XwlResultErrorEnum.SUCCESS.getCode(), XwlResultErrorEnum.SUCCESS.getMsg());
}
/**
* 无备注服务器异常
*
* @param <T>
* @return
*/
public static <T> R<T> serverError() {
return result(null, XwlResultErrorEnum.ERROR.getCode(), XwlResultErrorEnum.ERROR.getMsg());
}
/**
* 自定义异常回执信息
*
* @param <T>
* @return
*/
public static <T> R<T> exceptionReturn(XwlResultErrorEnum xwlResultErrorEnum) {
return result(null, xwlResultErrorEnum.getCode(), xwlResultErrorEnum.getMsg());
}
/**
* 统一封装异常体
*
* @param data
* @param code
* @param message
* @param <T>
* @return
*/
public static <T> R<T> result(T data, int code, String message) {
R<T> r = new R<>();
r.setCode(code);
r.setData(data);
r.setMessage(message);
return r;
}
}
自定义异常处理类
import com.xwl.config.enums.XwlResultErrorEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;
/**
* @author xueWenLiang
* @date 2021/4/30 14:20
* @Description 描述信息
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class XwlException extends RuntimeException {
/**
* 枚举异常控制
*/
private XwlResultErrorEnum xwlResultErrorEnum;
}
异常枚举类
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
/**
* @author xueWenLiang
* @date 2021/4/30 14:23
* @Description 描述信息
*/
@Getter
@NoArgsConstructor
@AllArgsConstructor
@SuppressWarnings("ALL")
public enum XwlResultErrorEnum {
SUCCESS(200, "操作成功"),
FAILURE(400, "业务异常"),
NOT_FOUND(404, "服务或资源未找到"),
ERROR(500, "服务异常"),
TOKEN_EXPIRE(401, "token过期,请重新登录!"),
FORBIDDEN(403, "访问被拒绝,您无权访问!"),
USER_NOT_NULL(404, "该用户不存在");
//异常编号
private Integer code;
//异常注释
private String msg;
}
全局异常拦截器
import com.xwl.config.exception.XwlException;
import com.xwl.utils.R;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author xueWenLiang
* @date 2021/4/30 14:35
* @Description 自定义拦截异常返回
*/
@ControllerAdvice
@ResponseBody
public class RequestErrorExceptionFilter {
/**
* 业务异常返回拦截并返回
* @param xwlException
* @return
*/
@ExceptionHandler(XwlException.class)
public R returnException(XwlException xwlException){
return R.exceptionReturn(xwlException.getXwlResultErrorEnum());
}
}
核心配置YML
#端口配置
server:
port: 8787
#数据库基础配置信息
spring:
datasource:
url: jdbc:mysql://localhost:3306/xwl?characterEncoding=utf-8
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
application:
name: 自定义注解服务
mybatis-plus:
configuration:
map-underscore-to-camel-case: true #开启驼峰命名规则支持
cache-enabled: true
#扫描mapper持久层接口
mapper-locations: classpath:com.xwl/**/mapper/*Mapper.xml
eureka:
client:
fetch-registry: false
register-with-eureka: false
启动类
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @author xueWenLiang
* @date 2021/4/29 16:26
* @Description 描述信息
*/
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.xwl.mapper")
public class CustomApplication {
public static void main(String[] args) {
SpringApplication.run(CustomApplication.class,args);
}
}
数据库脚本
库名:xwl
DROP TABLE IF EXISTS `xwl_user`;
CREATE TABLE `xwl_user` (
`id` bigint(50) NOT NULL,
`user_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL,
`user_address` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of xwl_user
-- ----------------------------
INSERT INTO `xwl_user` VALUES (1, '张三', '河南省');
INSERT INTO `xwl_user` VALUES (2, '李四', '湖南省');
SET FOREIGN_KEY_CHECKS = 1;
工程结构图
测试
不传token
传错误token
传正确(张三)token
传李四的token
结语
可以看到,有了自定义用户注解,可以统一获取用户信息体,无需一层一层的实现再去查库等操作,后续也可以拓展放置缓存,随时随地获取当前登录用户,这样的话解决一些日常业务问题还是很方便的,你学会了吗?
源码地址
https://github.com/xwlgithub/xuewenliang/tree/master/my-annotation