说一说java中的自定义注解之设计及实现

news2024/12/27 13:07:07

一、需求背景

比如我们需要对系统的部分接口进行token验证,防止对外的接口裸奔。所以,在调用这类接口前,先校验token的合法性,进而得到登录用户的userId/role/authority/tenantId等信息;再进一步对比当前用户是否有权限调用该接口。

但是,不是所有的接口都需要token校验,我们应该按需配置,能够支持排除掉无需token校验的接口。

本文的重点是讲述,如果让业务方开启token校验,不会涉及到如何去做权限及接口配置等方面。

因为,接口配置,我们是建议放在api网关层(不应该放在具体的微服务里),而实际生产中,不同的业务会有不同的api网关。

二、目标

  • 接口支持token校验与否的开关控制
  • 编写一个自定义注解
  • 对业务方透明,简单易用

三、总体设计

  • 建议的方案
    在这里插入图片描述
  • 本文所说的方案
    在这里插入图片描述

四、注解的定义

  • EnableJwtAuth.java
import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({EnableJwtAuthImportSelector.class})
public @interface EnableJwtAuth {

}
  • 增加开关
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({EnableJwtAuthImportSelector.class})
@ConditionalOnProperty(name = "spring.jwt.enabled", havingValue = "true", matchIfMissing = true)
public @interface EnableJwtAuth {

}
  • EnableJwtAuthImportSelector.java

import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

// 实现接口EnvironmentAware和ImportSelector
public final class EnableJwtAuthImportSelector implements ImportSelector, EnvironmentAware {

    private Environment environment;

    protected Environment getEnvironment() {
        return this.environment;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

// 读取配置项spring.jwt.enabled的值,默认是开启jwt校验
    protected boolean isEnabled() {
        return getEnvironment().getProperty("spring.jwt.enabled", Boolean.class, Boolean.TRUE);
    }

    @Override
    public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
        if (!isEnabled()) {
            return new String[0];
        }

        Class<EnableJwtAuth> annoType = EnableJwtAuth.class;
        Map<String, Object> annotationAttributes = importingClassMetadata
                .getAnnotationAttributes(annoType.getName(), false);
        AnnotationAttributes attributes = AnnotationAttributes
                .fromMap(annotationAttributes);
        
        Assert.notNull(attributes, String.format(
                "@%s is not present on importing class '%s' as expected",
                annoType.getSimpleName(), importingClassMetadata.getClassName()));

// 实例化两个类
        List<String> classNames = new ArrayList<>(2);
        classNames.add(GsonAutoConfiguration.class.getName());
        // JwtAuthConfiguration是我们自定义的类
        classNames.add(JwtAuthConfiguration.class.getName());
     
        return classNames.toArray(new String[0]);
    }

}
  • JwtAuthConfiguration.java
    此类中定义你所需要的java类。
    另外一点,如果你需要对接口uri进行过滤,也需要在这里进行校验。
    
    @Bean
    public JwtAuthenticationProvider jwtAuthenticationManager() {
        return new JwtAuthenticationProvider();
    }
  • JwtAuthenticationProvider.java

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.util.StringUtils;

/**
 * 依赖spring security 权限框架
 *
 * @author xxx
 */
public class JwtAuthenticationProvider implements AuthenticationProvider {

    private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationProvider.class);

   
    @Override
    public boolean supports(Class<?> authentication) {
        return JwtAuthenticationToken.class.isAssignableFrom(authentication);
    }

    @Override
    public Authentication authenticate(Authentication authenticationToken)  {
        final String authToken = (String) authenticationToken.getCredentials();
        if (StringUtils.isEmpty(authToken)) {
            throw new AuthenticationCredentialsNotFoundException(MessageDefs.TOKEN_MISSING);
        }

        // 调用认证服务进行token校验,示意图见上面的总体设计
        // 下面是伪代码,自定义类JwtUser用于包装用户信息
        JwtUser userDetails = tokenVerifyClient.verify(authToken);
    
        // 取出需要的字段,传递给TransmittedUserInfo透传对象
        TransmittedUserInfo userInfo = new TransmittedUserInfo(userDetails);

        XxTransmittableThreadLocal<TransmittedUserInfo> xxTransmittableThreadLocal = (XxTransmittableThreadLocal<TransmittedUserInfo>) ApplicationContextProvider
                .getApplicationContext()
                .getBean("xxTransmittableThreadLocal");
                
        xhTransmittableThreadLocal.set(userInfo);

        // 校验成功
        return new JwtAuthenticationToken(userDetails.getId(), null, userDetails.getAuthorities());
    }

}

五、自定义注解的使用

在这里插入图片描述

六、说在最后的话

自定义注解,本身比较简单,这里使用了@Import注解,故不需要再在META-INF/spring.factories增加org.springframework.boot.autoconfigure.EnableAutoConfiguration配置类。

我这里想要补充说明的是,文章里的权限校验,只是一种实现方案。
更建议你在api网关中实现token的校验。

那么,java服务中,需要做哪些工作呢?

把api网关传过来的字段,很好地传承并透传至下游服务。

还有一个重要的工作就是,取出当前服务上下文中的数据,做以下工作:

  • 用户ID是否和token一致,防止token被冒用
  • 接口的权限(判断当前用户是否能够访问该接口)
  • 取出当前用户的角色、租户ID、用户ID、学校ID等关键字段,保存到数据库,并输出打印日志。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/797062.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MyBatis 快速入门【中】

&#x1f600;前言 本篇博文是MyBatis(简化数据库操作的持久层框架)–快速入门[上]的核心部分&#xff0c;分享了MyBatis实现sql的xml配置和一些关联配置、异常分析 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&a…

软件外包开发的需求分析

需求分析是软件开发中的关键步骤&#xff0c;其目的是确定用户需要什么样的软件&#xff0c;以及软件应该完成哪些任务。需求分析是软件工程的早期工作&#xff0c;也是软件项目成功的基础&#xff0c;因此花费大量精力和时间去做好需求分析是值得的。今天和大家分享软件需求分…

数字孪生-数字城市效果实现方法

数字孪生-数字城市效果实现方法 效果图&#xff1a; 一、效果分析&#xff1a; .0 1、城市非主展示区域白模快速生成方案&#xff1a; 参考视频&#xff1a; 1、CityEngine 引用数据源生成。 cityengine2022一键生成城市模型&#xff0c;不用再用blendergis_哔哩哔哩_bil…

Python运算符列表及其优先顺序、结合性

本文表格对Python中运算符的优先顺序进行了总结&#xff0c;从最高优先级&#xff08;最先绑定&#xff09;到最低优先级&#xff08;最后绑定&#xff09;。相同单元格内的运算符具有相同优先级。除非句法显式地给出&#xff0c;否则运算符均指二元运算。 相同单元格内的运算…

数据安全之全景图系列——数据分类分级落地实践

1、数据分类分级现状 我们正处于一个数据爆炸式增长的时代&#xff0c;随着产业数字化转型升级的推进&#xff0c;数据已被国家层面纳入生产要素&#xff0c;并且成为企业、社会和国家层面重要的战略资源。数据分类分级管理不仅是加强数据交换共享、提升数据资源价值的前提条件…

Unreal MorphTarget Connect Bone MetaData Curve功能学习

MorphTarget Connected Bone和MetaData Curve是两个较冷门功能&#xff0c;近期在制作一些功能时留意到这2个内容&#xff0c;故研究一下。 1.MorphTarget Connected Bone 在骨架编辑面板中&#xff0c;选中MorphTarget时&#xff0c;可找到Connected Bone选项&#xff1a; …

[数据库]对数据库事务进行总结

文章目录 1、什么是事务2、事务的特性&#xff08;ACID&#xff09;3、并发事务带来的问题4、四个隔离级别&#xff1a; 1、什么是事务 事务是逻辑上的一组操作&#xff0c;要么都执行&#xff0c;要么都不执行。 事务最经典也经常被拿出来说例子就是转账了。假如小明要给小红…

图解SQL基础知识,小白也能看懂的SQL文章

本文介绍关系数据库的设计思想&#xff1a; 在 SQL 中&#xff0c;一切皆关系。 在计算机龄域有许多伟大的设计理念和思想&#xff0c;例如&#xff1a; 在 Unix 中&#xff0c;一切皆文件。 在面向对象的编程语言中&#xff0c;一切皆对象。 关系数据库同样也有自己的设计…

mybatis_使用注解开发

第一步&#xff1a;使用注解写一个接口 Select("select * from user")List<User> getUsers(); 第二步&#xff1a;绑定接口 第三步&#xff1a;测试 官方提示&#xff1a; 使用注解来映射简单语句会使代码显得更加简洁&#xff0c;但对于稍微复杂一点的语句&…

Spring AOP (面向切面编程)原理与代理模式—实例演示

一、AOP介绍和应用场景 Spring 中文文档 (springdoc.cn) Spring | Home 官网 1、AOP介绍&#xff08;为什么会出现AOP &#xff1f;&#xff09; Java是一个面向对象&#xff08;OOP&#xff09;的语言&#xff0c;但它有一些弊端。虽然使用OOP可以通过组合或继承的方…

使用网络 IP 扫描程序的原因

随着网络不断扩展以满足业务需求&#xff0c;高级 IP 扫描已成为网络管理员确保网络可用性和性能的关键任务。在大型网络中扫描 IP 地址可能具有挑战性&#xff0c;这些网络通常包括具有动态 IP、多个 DNS、DHCP 配置和复杂子网的有线和无线设备。使用可提供全面 IP 地址管理 &…

哈尔滨的全年平均气温和降水特点

哈尔滨全年平均气温和降水特点 哈尔滨是我国最靠北的省会城市&#xff0c;冬季那边特别冷&#xff0c;但夏季比较凉爽&#xff0c;下面根据中国气象台的1981-2010年的统计数据[1]&#xff0c;分析哈尔滨全年平均气温和降水的特点有以下几点&#xff1a; 1.全年最高气温在7月&…

ubuntu18.04安装autoware1.15

目录 前言一、准备工作1.安装autoware1.152.安装依赖3.把src/autoware/common/autoware_build_flags/cmake文件夹下的CUDA版本改为11.4&#xff08;或者你电脑上的版本&#xff09; 二、解决报错错误类型1错误类型2错误类型3错误类型4错误类型5错误类型6 前言 本文参考链接&am…

如何降低TCP在局域网环境下的数据传输延迟

以Ping为例。本案例是一个测试题目&#xff0c;只有现象展示&#xff0c;不含解决方案。 ROS_Kinetic_26 使用rosserial_windows实现windows与ROS master发送与接收消息_windows 接收ros1 消息 什么是ping&#xff1f; AI&#xff1a; ping是互联网控制消息协议&#xff08;…

数学建模-预测模型 神经网络

设置测试集&#xff0c;算sse&#xff0c;进行过拟合检验

无涯教程-jQuery - jQuery.get( url, data, callback, type )方法函数

jQuery.get(url&#xff0c;[data]&#xff0c;[callback]&#xff0c;[type])方法使用GET HTTP请求从服务器加载数据。 该方法返回XMLHttpRequest对象。 jQuery.get( url, [data], [callback], [type] ) - 语法 $.get( url, [data], [callback], [type] ) 这是此方法使用的…

电动汽车市场的减速,正在让小鹏汽车付出代价

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 总结&#xff1a; &#xff08;1&#xff09;由于价格压力上升、竞争加剧和需求减弱&#xff0c;小鹏汽车的交付量出现了明显下滑&#xff0c;6月份的交付量已经同比下降了43%。 &#xff08;2&#xff09;小鹏汽车对2023年…

ancos注册中心、网关和静态化freemarker、对象存储服务MinIO

1、docker安装ancos ①&#xff1a;docker拉取镜像 docker pull nacos/nacos-server:1.2.0②&#xff1a;创建容器 docker run --env MODEstandalone --name nacos --restartalways -d -p 8848:8848 nacos/nacos-server:1.2.0③&#xff1a;访问地址&#xff1a;http://192…

【论文阅读】The Deep Learning Compiler: A Comprehensive Survey

论文来源&#xff1a;Li M , Liu Y , Liu X ,et al.The Deep Learning Compiler: A Comprehensive Survey[J]. 2020.DOI:10.1109/TPDS.2020.3030548. 这是一篇关于深度学习编译器的综述类文章。 什么是深度学习编译器 深度学习&#xff08;Deep Learning&#xff09;编译器将…

getInputStream has already been called for this request 问题记录

问题背景 HttpServletRequest.getReader() HttpServletRequest.getInputStream() 不能在过滤器中读取一次二进制流&#xff08;字符流&#xff09;&#xff0c;又在另外一个Servlet中读取一次&#xff0c;即一个InputSteam(BufferedReader)对象在被读取完成后&#xff0c;将无…