一种自定义注解的用户鉴权方式
文章目录
- 一种自定义注解的用户鉴权方式
- 相关技术
- 用户鉴权
- 常规做法
- 自定义注解的用户鉴权方式
- 自定义注解
- 拦截器
- 配置
- 使用
- 附录
- JwtUtils
相关技术
- SpringBoot
- JWT
用户鉴权
在一些小项目中,对于不同的REST接口,需要限定用户身份,即哪类用户可访问,哪类不可访问。
常规做法
在一个接口接收一个请求后,自行解析其用户身份
@Resource
private HttpServletRequest request;
@GetMapping("/hello")
public String hello() {
String token = request.getHeader("token");
String userIdentify = JwtUtils.verify(token).getClaim(USER_IDENTIFY).asString();
if (!userIdentify.equals("STUDENT")){
return "无权操作";
}
return "hello";
}
这样可以实现功能需求,但是存在一个问题,每有一个需要鉴权的接口,就需要写一次鉴权代码。
当然,也可以封装成一个公用方法,大概形式如下:
public boolean auth(List<String> permissionList, HttpServletRequest request) {
String token = request.getHeader("token");
String userIdentify = JwtUtils.verify(token).getClaim(USER_IDENTIFY).asString();
return permissionList.contains(userIdentify);
}
但每次调用时,仍需要写大量重复代码
public String test2() {
List<String> authList = Arrays.asList("STUDENT");
if (!auth(authList)) {
return "无权操作";
}
return "hello";
}
那么,这个重复代码,能不能避免呢。答案很显然是可以的,由于每次都在数据处理之前进行鉴权,因此可以考虑使用拦截器。
自定义注解的用户鉴权方式
使用拦截器时,拦截器只有一套,但接口有多个,这时就需要一个参数的传递,使拦截器知道需要放行哪些身份的用户。
这时,需要先定义一个注解,需要鉴权时,只需要在接口上加入该注解,并设定预定值,即可告知拦截器需要放行的用户。
自定义注解
/**
* @author MingYi
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Auth {
String[] identify() default "STUDENT";
}
拦截器
/**
* @author MingYi
*/
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Auth auth = handlerMethod.getMethodAnnotation(Auth.class);
if (auth == null) {
return true;
}
String[] identify = auth.identify();
String token = request.getHeader("token");
// 这里仅做比对,如有权限等级等需求,可自行修改校验逻辑
String userIdentify = JwtUtils.verify(token).getClaim(USER_IDENTIFY).asString();
for (String i : identify) {
if (userIdentify.equals(i)) {
return true;
}
}
//返回错误信息
String json = new ObjectMapper().writeValueAsString("无权操作"));
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
return false;
}
return HandlerInterceptor.super.preHandle(request, response, handler);
}
}
在SpringBoot项目中,拦截器需要进行配置才可生效
配置
/**
* @author MingYi
*/
@Configuration
public class AuthConfig implements WebMvcConfigurer {
@Resource
private AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/user/**"); //放行user路径下的接口,否则可能无法进行注册及登录
}
}
使用
此时,我们再写一个接口,只需要在接口上方加上一个注解,即可完成权限校验操作。
e.g.
@Resource
private HttpServletRequest request;
@Auth(identify = {"ADMIN"})
@GetMapping("/hello")
public String hello() {
return "hello";
}
此时,即完成了使用自定义注解的方式进行用户鉴权的操作。
附录
JwtUtils
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import java.util.Calendar;
import java.util.Map;
/**
* 本类中包括JWT相关操作,如生成、解析及鉴权等。
*
* @author MingYi
*/
@Slf4j
@Component
@PropertySource("classpath:prop.properties")
public class JwtUtils {
/**
* 生成JWT时所用到的私钥。
* <p>请在resource/prop.properties中配置"jwt.signature",否则可能将无法使用。</p>
*/
private static String SIGNATURE;
public static final int ONE_DAY = 60 * 60 * 24;
@Value("${jwt.signature}")
private void setSignature(String signature) {
JwtUtils.SIGNATURE = signature;
}
/**
* 生成token,放在payload中的数据可以通过map方式传输,Utils的通用性会比较高,调用的时候需要多写一点
* SignatureVerificationException 无效签名Exception
* TokenExpiredException token过期Exception
* AlgorithmMismatchException token算法不一致Exception
*
* @param map 参数列表
* @return token
*/
public static String getToken(Map<String, String> map) {
JWTCreator.Builder builder = JWT.create();
if (map != null && map.size() != 0){
map.forEach((k, v) -> {
builder.withClaim(k, v);
});
}
Calendar instance = Calendar.getInstance();
//设定1天的token有效期
instance.add(Calendar.SECOND, ONE_DAY);
String token = builder.withExpiresAt(instance.getTime())
.sign(Algorithm.HMAC256(SIGNATURE));
log.info("Generate token successfully.");
return token;
}
/**
* 验证token的合法性
* 这里面会抛出各种异常,需要外部,如拦截器等进行捕获、处理
*
* @param token token
* @return 解析过的token信息
*/
public static DecodedJWT verify(String token) {
return JWT.require(Algorithm.HMAC256(SIGNATURE)).build().verify(token);
}
}
更多推荐
一种自定义注解的用户鉴权方式
发布评论