SpringBoot使用自定义注解校验Token及角色权限

10 篇文章 0 订阅
6 篇文章 0 订阅

最近学习时,自己项目中用到了Token,涉及到User和Admin两个角色,普通的用户并没有很大的权限,和Admin拥有较大的权限。每次在写代码前都需要重复校验角色,根据角色来决定是否有操作这个接口的权限。

于是我想到,能不能定义一个注解,在注解的参数中输入这个接口可访问的角色,同时再定义一个接口,得到对应token的参数。

项目没有涉及到RBAC模式,就划分Admin和User,因此思路还是比较好理解的。

1.定义一个Token注解


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 自定义注解,实现Token的校验
 * 2021-9-23 09:39:37
 * @author weilinlin
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Token {
    public static final String USER_KEY = "USER_ID";
    //开关,默认打开,设为false时关闭
    boolean validate() default true;
    //角色信息,支持多个角色
    String roles() ;

}

2.设定第二个注解,用于在解析Token时,返回对应的数据,如Id、role

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义注解,获取用户的信息
 * 2021-9-23 09:39:37
 * @author weilinlin
 */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginUser {
}

3.编写解析逻辑:,需要将解析后的数据放到request中

import entity.BaseController;
import entity.StatusCode;
import entity.TokenEntity;
import exception.CourseException;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import utils.ComStringUtil;
import utils.JwtUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@Component
public class JwtInterceptor extends HandlerInterceptorAdapter {
    private static final Logger logger = LoggerFactory.getLogger(BaseController.class);
    public static final String USER_KEY = "SMOYU";
    @Autowired
    private JwtUtil jwtUtil;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Token annotation;
        if(handler instanceof HandlerMethod) {
            annotation = ((HandlerMethod) handler).getMethodAnnotation(Token.class);
        }else{
            return true;
        }
        //没有声明需要权限,或者声明不验证权限
        if(annotation == null || annotation.validate() == false){
            return true;
        }

        //没有传送角色信息,默认为不校验权限
        if(ComStringUtil.isEmpty(annotation.roles())){
            return true;
        }

        //从header中获取token
        String header = request.getHeader("Authorization");
        if(ComStringUtil.isEmpty(header)){
            throw new CourseException(StatusCode.ACCESSERROR,"权限不足!");
        }
        logger.info("请求头:"+header);
        //得到token字符串
        String token = ComStringUtil.getTokenBySubs(header);
        if(ComStringUtil.isEmpty(token)){
            throw new CourseException(StatusCode.ACCESSERROR,"权限不足!");
        }
        //解析token
        Claims claims = null;
        try {
            claims = jwtUtil.parseJWT(token);
        }catch (Exception e){

            throw new CourseException(StatusCode.ACCESSERROR,"Token校验失败!请重新登录");
        }
        if(claims == null){

            throw new CourseException(StatusCode.ACCESSERROR,"Token校验失败!请重新登录");
        }
        logger.info("Token解析数据:"+claims);

        //得到角色信息,只要有一个角色符合,就能返回
        String[] roles = annotation.roles().split(",");
        logger.info("角色参数:"+roles);
        TokenEntity entity = new TokenEntity();

        for (String role : roles) {
            if(!role.equals(claims.get("roles"))){
                continue;
            }
            entity.setId(Long.valueOf(claims.getId()));
            entity.setRoles((String) claims.get("roles"));
            request.setAttribute(USER_KEY,entity);
            return  true;
        }

        throw new CourseException(StatusCode.ACCESSERROR,"权限不足!请使用正确的账号操作。");

    }
}

4.从request中拿数据

import com.smoyu.user.annotation.LoginUser;
import entity.TokenEntity;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import java.util.Map;

@Component
public class LoginUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver
{
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        return methodParameter.getParameterType().isAssignableFrom(TokenEntity.class)&&methodParameter.hasParameterAnnotation(LoginUser.class);
    }

    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
        //获取登陆用户信息

        Object object = nativeWebRequest.getAttribute(AuthorizationInterceptor.USER_KEY, RequestAttributes.SCOPE_REQUEST);
        if(object == null){
            return null;
        }
        return (TokenEntity)object;
    }
}

5.拦截对应的路径即可:

@Configuration
public class JwtConfig extends WebMvcConfigurationSupport {


    @Autowired
    private AuthorizationInterceptor jwtInterceptor;
    @Autowired
    private LoginUserHandlerMethodArgumentResolver loginUserHandlerMethodArgumentResolver;
//    private String fileSavePath = "/usr/xiaomoyu/images/";

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/**/login/**");
        super.addInterceptors(registry);
    }
    /**
     * 发现如果继承了WebMvcConfigurationSupport,则在yml中配置的相关内容会失效。 需要重新指定静态资源
     *
     * @param registry
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations(
                "classpath:/static/");
        registry.addResourceHandler("swagger-ui.html").addResourceLocations(
                "classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations(
                "classpath:/META-INF/resources/webjars/");
        //设定本地文件访问路径
    /*  registry.addResourceHandler("/images/**")
                .addResourceLocations("file:"+fileSavePath);*/
        super.addResourceHandlers(registry);
    }

    /**
     * 实现LoginUser注解,从请求request中得到参数
     * @param argumentResolvers
     */
    @Override
    protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(loginUserHandlerMethodArgumentResolver);
    }


}

使用方式

    @GetMapping("/userInfo/token")
    @Token(roles = "student")
    public Result getUserInfo(@LoginUser TokenEntity tokenEntity){
}

加入Token,roles如果有多个角色就用逗号隔开。
在方法参数上加入注解@LoginUser即可。

大功告成

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot项目中实现Token校验,可以通过以下步骤: 1. 在项目中引入相应的依赖,如JWT库等。 2. 创建Token的生成和校验工具类,其中生成Token的方法可以使用JWT库中的方法,校验Token的方法可以解析Token中的信息并进行校验。 3. 在需要进行Token校验的接口中,添加一个拦截器,用于拦截请求并进行Token校验。在拦截器中获取请求头中的Token信息,然后调用Token校验工具类进行校验。 4. 如果Token校验通过,则放行请求;如果Token校验失败,则返回相应的错误信息。 以下是一个简单的示例代码: (1)pom.xml中添加JWT库的依赖: ```xml <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency> ``` (2)创建Token工具类: ```java import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.Date; import java.util.HashMap; import java.util.Map; @Component public class JwtTokenUtil { private static final String CLAIM_KEY_USERNAME = "sub"; private static final String CLAIM_KEY_CREATED = "created"; @Value("${jwt.secret}") private String secret; @Value("${jwt.expiration}") private Long expiration; public String generateToken(String username) { Map<String, Object> claims = new HashMap<>(); claims.put(CLAIM_KEY_USERNAME, username); claims.put(CLAIM_KEY_CREATED, new Date()); return generateToken(claims); } private String generateToken(Map<String, Object> claims) { Date expirationDate = new Date(System.currentTimeMillis() + expiration * 1000); return Jwts.builder() .setClaims(claims) .setExpiration(expirationDate) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } public String getUsernameFromToken(String token) { String username; try { Claims claims = getClaimsFromToken(token); username = claims.getSubject(); } catch (Exception e) { username = null; } return username; } public boolean validateToken(String token, String username) { String usernameFromToken = getUsernameFromToken(token); return usernameFromToken.equals(username) && !isTokenExpired(token); } private Claims getClaimsFromToken(String token) { Claims claims; try { claims = Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); } catch (Exception e) { claims = null; } return claims; } private boolean isTokenExpired(String token) { Date expirationDate = getExpirationDateFromToken(token); return expirationDate.before(new Date()); } private Date getExpirationDateFromToken(String token) { Claims claims = getClaimsFromToken(token); return claims.getExpiration(); } } ``` (3)创建Token拦截器: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component public class JwtTokenInterceptor implements HandlerInterceptor { @Autowired private JwtTokenUtil jwtTokenUtil; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authorization"); String username = jwtTokenUtil.getUsernameFromToken(token); if (username != null && jwtTokenUtil.validateToken(token, username)) { return true; } else { response.getWriter().write("Token is invalid"); return false; } } } ``` (4)在需要进行Token校验的接口中添加拦截器: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api") public class MyController { @Autowired private JwtTokenInterceptor jwtTokenInterceptor; @RequestMapping("/test") public String test() { return "Hello, world!"; } @RequestMapping("/test2") public String test2() { return "Hello, world 2!"; } @RequestMapping("/test3") public String test3() { return "Hello, world 3!"; } @RequestMapping("/test4") public String test4() { return "Hello, world 4!"; } @RequestMapping("/test5") public String test5() { return "Hello, world 5!"; } } ``` 在Spring Boot项目中实现Token校验,可以通过以上步骤实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值