关于java:一行代码就可以实现-Jwt-登录认证爽呆了

6次阅读

共计 7205 个字符,预计需要花费 19 分钟才能阅读完成。

在上一篇《Spring Boot 集成 JWT 实现用户登录认证》中,咱们曾经基于 nimbus-jose-jwt 封装好了一个 jwt,并且只须要自定义 HandlerInterceptor 实现类和 WebMvcConfigurer 实现类就能够在我的项目中引入应用 jwt 做登录认证。

Spring Boot 中的 starter 是一种十分重要的机制,可能摈弃以前繁冗的配置,将其对立集成进 starter,利用只须要在 Maven 中引入 starter 依赖,Spring Boot 就能主动扫描到要加载的信息并启动相应的默认配置。starter 让咱们解脱了各种依赖库的解决,须要配置各种信息的困扰。Spring Boot 会主动通过 classpath 门路下的类发现须要的 Bean,并注册进 IOC 容器。Spring Boot 提供了针对日常企业应用研发各种场景的 spring-boot-starter 依赖模块。所有这些依赖模块都遵循着约定成俗的默认配置,并容许咱们调整这些配置,即遵循“约定大于配置”的理念。

接下来,咱们就在之前封装的 ron-jwt 根底上自定义的一个 starter,我的项目只有在依赖中引入这个 starter,再定义简略的配置就能够应用 jwt 的实现登录认证。

新建工程并配置依赖

Spring Boot 提供的 starter 以 spring-boot-starter-xxx 的形式命名的。官网倡议自定义的 starter 应用 xxx-spring-boot-starter 命名规定,以辨别 Spring Boot 生态提供的 starter。所以咱们新建工程 ron-jwt-spring-boot-starter。

在 pom.xml 中引入依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-configuration-processor</artifactId>
  <optional>true</optional>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>io.ron</groupId>
  <artifactId>ron-jwt</artifactId>
  <version>1.0-SNAPSHOT</version>
</dependency>

spring-boot-configuration-processor 次要的作用是在编译时在 META-INF 下生成 spring-configuration-metadata.json 文件,该文件次要为 IDE 应用,即能够通过在 application.properties 文件中通过 ctrl + 点击进入配置属性所在的类中。

spring-boot-autoconfigure 次要作用是提供主动拆卸性能。

spring-boot-starter-web 则是因为咱们将会内置 HandlerInterceptor 实现类和 WebMvcConfigurer 实现类。

ron-jwt 是咱们在上一篇中封装好的 jwt 库。

定义配置项治理类

咱们定义 JwtProperties 来申明 starter 的使用者可应用哪些配置项。

@ConfigurationProperties(prefix = "ron.jwt")
public class JwtProperties {

    private String tokenName = JwtUtils.DEFAULT_TOKEN_NAME;

    private String hmacKey;

    private String jksFileName;

    private String jksPassword;

    private String certPassword;

    // 签发人
    private String issuer;

    // 主题
    private String subject;

    // 受众
    private String audience;

    private long notBeforeIn;

    private long notBeforeAt;

    private long expiredIn;

    private long expiredAt;
}

具体参数阐明,可参考上一篇文章中的 JwtConfig。

@ConfigurationProperties 注解指定了所有配置项的前缀为 ron.jwt

@ConfigurationProperties 的根本用法非常简单:咱们为每个要捕捉的内部属性提供一个带有字段的类。请留神以下几点:

  • 前缀定义了哪些内部属性将绑定到类的字段上。
  • 依据 Spring Boot 宽松的绑定规定,类的属性名称必须与内部属性的名称匹配。
  • 咱们能够简略地用一个值初始化一个字段来定义一个默认值。
  • 类自身能够是包公有的。
  • 类的字段必须有公共 setter 办法。

Spring Boot 宽松的绑定规定(relaxed binding):

Spring Boot 应用一些宽松的绑定属性规定。因而,以下变体都将绑定到 tokenName 属性上:

  • ron.jwt.tokenname=Authorization
  • ron.jwt.tokenName=Authorization
  • ron.jwt.token_name=Authorization
  • ron.jwt.token-name=Authorization

实现相干的性能

在上一篇中,咱们把 HandlerInterceptor 实现类和 WebMvcConfigurer 实现类放在具体的业务我的项目中本人实现。实际上这部分也是我的项目中比拟通用的逻辑,因而咱们思考将这些实现搁置在 starter 中。我的项目中能够不做多余的自定义,间接通过引入 starter 就能够应用 jwt 认证的性能。

JwtInterceptor

public class JwtInterceptor implements HandlerInterceptor {private Logger logger = LoggerFactory.getLogger(JwtInterceptor.class);

    private static final String PREFIX_BEARER = "Bearer";

    @Autowired
    private JwtProperties jwtProperties;

    @Autowired
    private JwtService jwtService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {

        // 如果不是映射到办法间接通过
        if(!(handler instanceof HandlerMethod)){return true;}

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        // 查看是否有 @AuthRequired 注解,有且 required() 为 false 则跳过
        if (method.isAnnotationPresent(AuthRequired.class)) {AuthRequired authRequired = method.getAnnotation(AuthRequired.class);
            if (!authRequired.required()) {return true;}
        }

        String token = request.getHeader(jwtProperties.getTokenName());

        logger.info("token: {}", token);

        if (StringUtils.isEmpty(token) || token.trim().equals(PREFIX_BEARER.trim())) {return true;}

        token = token.replace(PREFIX_BEARER, "");

        // 设置线程局部变量中的 token
        JwtContext.setToken(token);

        // 在线程局部变量中设置实在传递的数据,如以后用户信息等
        String payload = jwtService.verify(token);
        JwtContext.setPayload(payload);

        return onPreHandleEnd(request, response, handler, payload);
    }

    public boolean onPreHandleEnd(HttpServletRequest request, HttpServletResponse response,
                                  Object handler, String payload) throws Exception {return true;}

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) throws Exception { }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) throws Exception {
        // 务必在线程完结前清理线程局部变量
        JwtContext.removeAll();}
}

onPreHandleEnd 办法是一个默认实现,如业务上有必要,也能够继承 JwtInterceptor,在这个办法中增加自定义的逻辑。一个可能的场景是将 JWT 的 token 放到 Redis 中进行超时治理。

JwtInterceptorConfig

public class JwtInterceptorConfig implements WebMvcConfigurer {

    private JwtInterceptor jwtInterceptor;

    public JwtInterceptorConfig(JwtInterceptor jwtInterceptor) {this.jwtInterceptor = jwtInterceptor;}

    @Override
    public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(jwtInterceptor).addPathPatterns("/**");
    }
}

这里默认拦挡了所有的申请,在上一篇文章中,咱们提到能够配合 @AuthRequired 来过滤不须要拦挡的申请。

编写主动配置逻辑

JwtAutoConfiguration

@Configuration
@EnableConfigurationProperties(JwtProperties.class)
public class JwtAutoConfiguration {

    @Autowired
    private JwtProperties jwtProperties;

    @Bean
    public JwtConfig jwtConfig() {JwtConfig jwtConfig = new JwtConfig();
        BeanUtils.copyProperties(jwtProperties, jwtConfig);
        return jwtConfig;
    }

    @Bean
    public JwtService jwtService() {JwtConfig jwtConfig = jwtConfig();
        return JwtUtils.obtainJwtService(jwtConfig);
    }

    @Bean
    public JwtInterceptor jwtInterceptor() {return new JwtInterceptor();
    }

    @Bean
    public JwtInterceptorConfig jwtInterceptorConfig() {return new JwtInterceptorConfig(jwtInterceptor());
    }
}

@EnableConfigurationProperties 的作用是引入应用 @ConfigurationProperties 注解的类,并使其失效。

@EnableConfigurationProperties 文档中解释:当 @EnableConfigurationProperties 注解利用到你的 @Configuration 时,任何被 @ConfigurationProperties 注解的 beans 将主动被 Environment 属性配置。这种格调的配置特地适宜与 SpringApplication 的内部 YAML 配置进行配合应用。

集成 starter 使之失效

有两种形式能够让 starter 在利用中失效。

通过 SPI 机制加载 – 被动失效

通过 Spring Boot 的 SPI 的机制来去加载咱们的 starter。

在 resources 目录下新建 WEB-INF/spring.factories 文件。

META-INF/spring.factories 文件是 Spring Boot 框架辨认并解析 starter 的外围文件。spring.factories 文件是帮忙 Spring Boot 我的项目包以外的 Bean(即在 pom 文件中增加依赖中的 Bean)注册到 Spring Boot 我的项目的 Spring 容器。因为 @ComponentScan 注解只能扫描 Spring Boot 我的项目包内的 Bean 并注册到 Spring 容器中,因而须要 @EnableAutoConfiguration 注解来注册我的项目包外的 Bean。而 spring.factories 文件,则是用来记录我的项目包外须要注册的 Bean 类名。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=io.ron.jwt.starter.JwtAutoConfiguration

自定义 Enable 注解引入 – 被动失效

在 starter 组件集成到咱们的 Spring Boot 利用时须要被动申明启用该 starter 才失效,咱们通过自定义一个 @Enable 注解而后在把主动配置类通过 Import 注解引入进来。

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

如果应用被动失效的形式,那么实现被动失效的 META-INF/spring.factories 文件须要移除。

打包并公布 starter

应用 mvn install 能够打包并装置在本地;

应用 mvn deploy 能够公布到近程仓库。

后续我会写一篇对于 Maven 的文章,联合本人入行多年的教训,将开发人员会用到 Maven 技巧进行梳理。

欢送关注我的公众号:精进 Java(ID:craft4j)。

测试应用程序

能够复用上一篇中的我的项目,在 pom.xml 中批改依赖,引入 ron-jwt-spring-boot-starter。

<dependency>
  <groupId>io.ron</groupId>
  <artifactId>ron-jwt-spring-boot-starter</artifactId>
  <version>1.0-SNAPSHOT</version>
</dependency>

去掉 HandlerInterceptor 实现类和 WebMvcConfigurer 实现类。

在 application.yml 中配置 ron.jwt.hmac-key 的值,就能够提供 HMAC 算法实现的 JWT 签名与验证性能了。

如果 starter 采纳的被动失效形式,当初就能够运行程序,而后应用 Postman 进行测试并察看后果。

如果 starter 采纳的被动失效形式,须要在我的项目启动类上增加 @EnableJwt 注解将 jwt-starter 引入。

@SpringBootApplication
public class JwtStarterApplication {public static void main(String[] args) {SpringApplication.run(JwtStarterApplication.class, args);
    }
}

– End –


通过两篇文章,咱们展现了基于 nimbus-jose-jwt 的 jwt 库应用,在此基础上封装了咱们本人的根底 jwt 库,并介绍了 Spring Boot 自定义 starter 的步骤,以及自定义 @Enable 注解的实现。

如果你感觉有播种,请关注我的公众号:精进 Java(ID:craft4j),第一工夫获取 Java 后端与架构的常识动静。

如果你对我的项目的残缺源码感兴趣,能够在公众号中回复 jwt 来获取。

正文完
 0