基于MVC轻量级的JWT权限验证

简单的JWT验证框架

JWT就不多介绍了

传统的shiro和SpringSecurity安全框架需要Session,重启后Session会丢失,或者将Session存入redis也行,但是相对比较繁琐。利用JWT可以很轻松的实现Token的认证,在前后端分离的情况下,JWT也显得非常的简易。

jwt的工具有很多,这里使用一种简单的双向加密[单向原理一模一样自己改一下jwtUtil就行]

依赖

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>

JWT工具:

    package com.chenyilei.demo.interceptor;
    
    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.JwtBuilder;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    import org.springframework.util.CollectionUtils;
    import org.thymeleaf.util.ArrayUtils;
    
    import java.util.Collections;
    import java.util.Date;
    import java.util.List;
    
    /**
     *
     * @author chenyilei
     * @email 705029004@qq.com
     * @date 2019/03/21- 12:49
     */
   public static class JwtUtil {
        //加密密钥
        private static String key = "!@bf5$chenyilei#GD'";
        //凭证过期时长
        private static long ttl = 6*3600*1000 ;//以毫秒为单位

        /**
         * 生成JWT
         *
         * @param id
         * @param subject
         * @return
         */
        public static String createJWT(String id, String subject, String[] authorites) {
            long nowMillis = System.currentTimeMillis();
            Date now = new Date(nowMillis);
            JwtBuilder builder = Jwts.builder()
                    .setId(id)
                    .setSubject(subject)
                    .setIssuedAt(now)
                    .signWith(SignatureAlgorithm.HS256, key).claim("authorities", authorites);
            if (ttl > 0) {
                builder.setExpiration( new Date( nowMillis + ttl));
            }
            return builder.compact();
        }

        /**
         * 解析JWT
         * @param jwtStr
         * @return
         */
        public static JwtInterceptor.UserInfo parseJWT(String jwtStr){
            io.jsonwebtoken.Claims body = Jwts.parser()
                    .setSigningKey(key)
                    .parseClaimsJws(jwtStr)
                    .getBody();
            JwtInterceptor.UserInfo userInfo = new JwtInterceptor.UserInfo();
            userInfo.setId(body.getId());
            userInfo.setUsername(body.getSubject());
            userInfo.setAuthorites( (List<String>)body.get("authorities") );
            return userInfo;
        }

    }

基于MVC的JWT拦截器

    /**
 *   权限拦截器
 *
 * @author chenyilei
 * @email 705029004@qq.com
 * @date 2019/03/21- 14:47
 */
public class JwtInterceptor implements HandlerInterceptor {

    //存放解析出来的user信息
    public static ThreadLocal<UserInfo> saveUserInfo = new ThreadLocal<>();

    private static volatile Map<Object,CacheInfo> methodCache = new ConcurrentHashMap<>(64);

    //解析有此注解的Controller或method,判断是否拦截,权限足够
    @Target({ ElementType.METHOD, ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public static @interface JwtAuth {
        boolean noToken() default false;
        String[] authorities() default {} ;
    }

    //用户信息
    @Data
    public static class UserInfo{
        private String id;
        private String username;
        private List<String> authorities;
    }
    //缓存方法的相关信息
    @Data
    public static class CacheInfo{
        private Method method;
        private boolean noToken ;
        private Set<String> authorities;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //不拦截静态文件或者错误定向
        if(!(handler instanceof HandlerMethod) || request.getRequestURI().startsWith("/error")){
            return true;
        }
        //判断有无权限注解
        Method method = ((HandlerMethod) handler).getMethod();
        Set<String> needAuthorities = null;
        boolean noToken = false;

        //判断有无缓存controller 或 方法上的注解信息
        CacheInfo isCached = methodCache.get(method);
        if(null != isCached){
            //有缓存
            noToken = isCached.isNoToken();
            needAuthorities = isCached.getAuthorities();
        }else{
            //无缓存的情况
            needAuthorities = new HashSet<>();
            JwtAuth jwtAuth = null;
            //先解析 Controller 上的注解 因为method优先于controller
            if( method.getDeclaringClass().isAnnotationPresent(JwtAuth.class) ){
                jwtAuth = method.getDeclaringClass().getDeclaredAnnotation(JwtAuth.class);
                if(jwtAuth.authorities().length > 0){
                    needAuthorities.addAll(Arrays.asList(jwtAuth.authorities()));
                }
                noToken = jwtAuth.noToken();
            }

            //再解析 Method 上的注解
            if ( method.isAnnotationPresent(JwtAuth.class) ) {
                jwtAuth = method.getDeclaredAnnotation(JwtAuth.class);
                if(jwtAuth.authorities().length > 0){
                    needAuthorities.addAll(Arrays.asList(jwtAuth.authorities()));
                }
                noToken = jwtAuth.noToken();
            }

            //加入缓存
            CacheInfo cacheInfo = new CacheInfo();
            cacheInfo.setMethod(method);
            cacheInfo.setNoToken(noToken);
            cacheInfo.setAuthorities(needAuthorities);
            methodCache.put(method,cacheInfo);
        }

        //不需要拦截
        if(noToken){
            return true;
        }

        //TODO:判断有无权限 cookie or header
        // String authorization = request.getHeader("Authorization");
        String cookieValue = CookieUtil.readCookie(request, "Authorization");
        if(null == cookieValue){
            //无权限
            throw new Exception("你需要一个权限凭证!");
        }

        //解析错误直接报错返回
        UserInfo userInfo = JwtUtil.parseJWT(cookieValue);

        //无权限要求,放行
        if( needAuthorities.isEmpty() ){
            saveUserInfo.set(userInfo);
            return true;
        }

        //有权限要求,检查
        if( userInfo.getAuthorities().containsAll(needAuthorities)){
            saveUserInfo.set(userInfo);
            return true;
        }

        throw new Exception("你所具备的权限不足!");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        saveUserInfo.remove();
    }

}

使用JwtAuth注解选择不用拦截的Controller 或 Method ,或者加上权限.

mvc配置:

    /**
     * --mvc config--
     *
     * @author chenyilei
     * @date 2019/03/21- 14:46
     */
    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            JwtInterceptor jwtInterceptor = new JwtInterceptor();
            registry.addInterceptor(jwtInterceptor).addPathPatterns("/**");
        }
    }

使用的大致方法…

@Controller
@JwtInterceptor.JwtAuth(noToken = true)
public class DemoController {

    @Autowired
    UserMapper userMapper;

    //可以直接访问
    @JwtInterceptor.JwtAuth(noToken = true)
    @RequestMapping("/test1")
    public ModelAndView test1(Map map){
        map.put("id",userMapper.selectAll().get(0).getUserId());
        map.put("name",userMapper.selectAll().get(0).getUserName());
        map.put("obj",userMapper.selectAll().get(0));
        return new ModelAndView("test/thy1",map);
    }
    //要有admin的权限才能访问
    @RequestMapping("/vueTest")
    @JwtInterceptor.JwtAuth(authorities = {"admin"})
    public ModelAndView test(Map map){
        return new ModelAndView("test/vue_01",map);
    }

    //Controller上有通过,不需要token可以访问
    @RequestMapping("/222")
    public ModelAndView test3(Map map){
        return new ModelAndView("/",map);
    }
}

www.chenyilei.cn

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值