redis jwt spring boot spring security 实现api token 验证

文章地址:http://www.haha174.top/article/details/258083

项目源码:https://github.com/haha174/jwt-token.git

具体的实际效果可以看考这里 目前已经部署一个 个人测试机器上面:

http://cloud.codeguoj.cn/api-cloud-server/swagger-ui.html#!/token45controller/loginUsingPOST

相信很多人都调用过api, 一般的大致基本步骤都是先用登陆获得一个token,然后使用token调用api 或者直接给你一个token 凭借token调用api.

那么这里实现一个spring security+ redis+ jwt 发token 和验证token的一个项目。

首先需要安装redis 如果不会的小伙伴可以参考http://www.haha174.top/article/details/257478 这篇博客。

创建一个maven项目导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.wen.security</groupId>
    <artifactId>jwt-token</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    </properties>
    <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-parent</artifactId>
           <version>1.5.6.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.6</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.6.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.44</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.7.0</version>
        </dependency>

    </dependencies>
</project>

编写 token 拦截器 通过重写springsecurity AbstractAuthenticationProcessingFilter

public class AuthTokenFilter extends AbstractAuthenticationProcessingFilter
{
  Logger logger = LoggerFactory.getLogger(this.getClass());

  @Value("${token.header}")
  private String tokenHeader;

  @Value("${token.name}")
  private String tokenName;

  public AuthTokenFilter(RequestMatcher matcher)
  {
    super(matcher);
  }

  @Override
  public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
      throws AuthenticationException, IOException, ServletException
  {
    String authToken = request.getHeader(this.tokenHeader);
    if(StringUtils.isEmpty(authToken))
    {
      authToken = request.getParameter(this.tokenName);
    }
    logger.debug("start to check token:{} *************");
    if (authToken == null)
    {
      throw new AuthenticationCredentialsNotFoundException("Access Token is not provided");
    }
    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(authToken, null);
    return getAuthenticationManager().authenticate(authentication);

  }

  @Override
  protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
      Authentication auth) throws IOException, ServletException
  {
    SecurityContext context = SecurityContextHolder.createEmptyContext();
    context.setAuthentication(auth);
    SecurityContextHolder.setContext(context);
    chain.doFilter(request, response);
  }

  @Override
  protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
      AuthenticationException authException) throws IOException, ServletException
  {
    SecurityContextHolder.clearContext();
    response.setStatus(HttpStatus.UNAUTHORIZED.value());
    response.setContentType(MediaType.APPLICATION_JSON_VALUE);
    String message;
    if (authException.getCause() != null)
    {
      message = authException.getCause().getMessage();
    }
    else
    {
      message = authException.getMessage();
    }

    BaseResponse baseResponse = new BaseResponse();
    baseResponse.setStatusCode(IConstants.RESPONSE_STATUS_CODE_FAILED);
    baseResponse.setStatusMsg(message);

    byte[] body = new ObjectMapper().writeValueAsBytes(baseResponse);
    response.getOutputStream().write(body);
  }
}

自定义校验

@Component
public class AuthTokenProvider implements AuthenticationProvider
{
  Logger logger = LoggerFactory.getLogger(this.getClass());

  @Autowired
  protected HttpServletRequest request;

  @Autowired
  TokenService tokenService;

  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException
  {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    if(auth!=null && auth.isAuthenticated())
    {
      return new UsernamePasswordAuthenticationToken(auth.getPrincipal(), null, new ArrayList<>());
    }
    String token = (String) authentication.getPrincipal();
    if (token != null)
    {
      if (!tokenService.checkToken(token))
      {
        throw new CredentialsExpiredException("Access Token is expired. Please login again.");
      }
    }
    else
    {
      throw new BadCredentialsException("Invalid token String.");
    }
    logger.debug("Authenticated successfully.");
    return new UsernamePasswordAuthenticationToken(token, null, new ArrayList<>());
  }

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

}

配置spring security 将上述的拦截器配置到spring security 的拦截器链上 和拦截规则

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebAuthConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Bean
    public AuthTokenFilter authenticationTokenFilterBean() throws Exception {
        List<String> pathsToSkip = Arrays.asList("/login");
        List<String> processingPath = Arrays.asList("/service/**","/logout");
        SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, processingPath);
        AuthTokenFilter filter = new AuthTokenFilter(matcher);
        filter.setAuthenticationManager(authenticationManager);
        return filter;
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("*"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                .antMatchers("/", "/*.html", "/**/favicon.ico", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
                .and().addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
        httpSecurity.cors().configurationSource(corsConfigurationSource());
        httpSecurity.logout().disable();
        httpSecurity.headers().cacheControl();

    }
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    CharacterEncodingFilter characterEncodingFilter() {
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        filter.setEncoding("UTF-8");
        filter.setForceEncoding(true);
        return filter;
    }
}

生成token 使用json web toekn

public class JavaWebToken {

    private static Logger log = LoggerFactory.getLogger(JavaWebToken.class);

    //该方法使用HS256算法和Secret:bankgl生成signKey
    private static Key getKeyInstance() {
        //We will sign our JavaWebToken with our ApiKey secret
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary("token");
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
        return signingKey;
    }

    //使用HS256签名算法和生成的signingKey最终的Token,claims中是有效载荷
    public static String createJavaWebToken(Map<String, Object> claims) {
        return   Jwts.builder()
                .setIssuer("Jersey-Security-Basic")//设置发行人
                .setSubject("subject")//设置抽象主题
                .setAudience("login")//设置角色
                .setExpiration(getDate())//过期时间
                .setIssuedAt(new Date())//设置现在时间
                .setClaims(claims)
                .signWith( SignatureAlgorithm.HS256,getKeyInstance())
                .compact();
    }

    //解析Token,同时也能验证Token,当验证失败返回null
    public static Map<String, Object> parserJavaWebToken(String jwt) {
        try {
            Map<String, Object> jwtClaims =
                    Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(jwt).getBody();
            return jwtClaims;
        } catch (Exception e) {
            log.error("json web token verify failed");
            return null;
        }
    }
    public static Date getDate(){
        try {
        /*    SimpleDateFormat format =  new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date date=format.parse(new Date().getTime()+new TokenProperties().getExpires()*1000+"");
            return date;*/
            long currentTime = System.currentTimeMillis() ;
            currentTime +=30*60*1000*2;
            Date date=new Date(currentTime);
            return date;
        }catch (Exception e){
            e.printStackTrace();
            long currentTime = System.currentTimeMillis() ;
            currentTime +=30*60*1000*2;
            Date date=new Date(currentTime);
            return date;
        }
    }
}

配置swagger

在请求头中加入header

 @Bean
    public Docket demoApi() {
        ParameterBuilder aParameterBuilder = new ParameterBuilder();
        aParameterBuilder.name("Authorization").description("input the token for authentication either in the authorization field or in the token field").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
        ParameterBuilder aParameterBuilder1 = new ParameterBuilder();
        aParameterBuilder1.name("token").description("input the token for authentication either in the authorization field or in the token field").modelRef(new ModelRef("string")).parameterType("query").required(false).build();
        List<Parameter> aParameters = new ArrayList<Parameter>();
        aParameters.add(aParameterBuilder.build());
        aParameters.add(aParameterBuilder1.build());
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(getApiInfo()).useDefaultResponseMessages(false).globalOperationParameters(aParameters)
                .select()  .apis(RequestHandlerSelectors.basePackage("com.wen.token.web")).build();
    }
    protected ApiInfo getApiInfo()
    {
      return new ApiInfo("Rest Web Service", "total api  Rest Web Service " + new Date(), "", "",
          new Contact("cxhc", "", ""), "", "",new ArrayList<VendorExtension>());
    }

整合redis 可以参考这篇博客

http://www.haha174.top/article/details/251216

只需要启动应用 访问http://localhost:8080/swagger-ui.html#/

----

原文地址:https://www.cnblogs.com/haha174/p/9032858.html

时间: 2024-10-07 22:35:25

redis jwt spring boot spring security 实现api token 验证的相关文章

Angular集成Spring Boot,Spring Security,JWT和CORS

本文介绍了Spring Boot的基本配置,Angular集成Spring Boot.Spring Security的方法.当前流行的JWT更适合与Angular集成,优于Spring Secuity提供的CSRF.另外引入了springfox-swagger和spring-boot-starter-actuator,演示了如何利用Swagger生成JSON API文档,如何利用Actuator监控应用. 本文前端基于Angular官方样例Tour of Heroes,请先到官网下载. 技术堆栈

Spring boot +Spring Security + Thymeleaf 认证失败返回错误信息

spring boot以其众多友谊的特性,如零配置.微服务等,吸引了很多的粉丝.而其与Spring Security安全框架的无缝结合,使其具备的安全的特性.在此基础上使用Thymeleaf模板引擎进行渲染,静动态结合,让页面开发更加简单.直观. 通过表单提交登录的用户名和密码是登录接口比较常见的一种设计.在初学的过程中,我也不例外的采用个这种方式.表单设计见下图. 登录成功,完成正常的主页面跳转,这个不存在问题.存在问题的是,登录失败了该咋办呢?我就在考虑,由于thymeleaf的局部刷新操作

255.Spring Boot+Spring Security:使用md5加密

说明 (1)JDK版本:1.8 (2)Spring Boot 2.0.6 (3)Spring Security 5.0.9 (4)Spring Data JPA 2.0.11.RELEASE (5)hibernate5.2.17.Final (6)MySQLDriver 5.1.47 (7)MySQL 8.0.12 需求缘起 很多时候,我们自己已经有现成的一套系统在运行了,这时候要接入spring security的话,那么难免会碰到一个问题:就是自己设计的密码加密方式和spring secur

256.Spring Boot+Spring Security: MD5是加密算法吗?

说明 (1)JDK版本:1.8 (2)Spring Boot 2.0.6 (3)Spring Security 5.0.9 (4)Spring Data JPA 2.0.11.RELEASE (5)hibernate5.2.17.Final (6)MySQLDriver 5.1.47 (7)MySQL 8.0.12 前言 有网友在公众号留言:准确的说md5是摘要算法不是加密算法 针对这个问题,当时也没有仔细的思考,空下来的时候,对于这个问题整理了下思路. 一.加密算法 1.1 加密和解密 1.1

Spring Boot集成smart-doc生成api文档

smart-doc是一个java restful api文档生成工具,smart-doc颠覆了传统类似swagger这种大量采用注解侵入来生成文档的实现方法.smart-doc完全基于接口源码分析来生成接口文档,完全做到零注解侵入,你只需要按照java标准注释的写,smart-doc就能帮你生成一个简易明了的markdown或是一个像GitBook样式的静态html文档.下面将介绍如何在Spring Boot项目中集成smart-doc生成一个简明的api文档. 注意: smart-doc已经被

Spring -&gt; Spring Boot &gt; Spring Cloud

Spring -> Spring Boot > Spring Cloud 这几天刚刚上班,公司用的是Spring Cloud,接触不多.我得赶快学起来. 想学习就必须得知道什么是微服务,什么是Spring Boot,什么是Spring Cloud,以及两者之间有什么关系? 什么是微服务? 简而言之,微服务架构风格是一种将单个应用程序作为一套小型服务开发的方法,每种应用程序都在自己的进程中运行,并与轻量级机制(通常是HTTP资源API)进行通信. 这些服务是围绕业务功能构建的,可以通过全自动部署

Spring Boot/Spring Cloud、ESB、Dubbo

如何使用Spring Boot/Spring Cloud 实现微服务应用spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中的配置管理.服务发现.断路器.智能路由.微代理.控制总线.全局锁.决策竞选.分布式会话和集群状态管理等操作提供了一种简单的开发方式. Spring Cloud与Dubbo对比提到Dubbo,我想顺便提下ESB,目前央视新华社也在用ESB来做任务编排,这里先比较下Dubbo和ESB: ESB(企业数据总线),一般采用集中式

spring boot 通过AOP防止API重复请求

实现思路 基于Spring Boot 2.x 自定义注解,用来标记是哪些API是需要监控是否重复请求 通过Spring AOP来切入到Controller层,进行监控 检验重复请求的Key:Token + ServletPath + SHA1RequestParas Token:用户登录时,生成的Token ServletPath:请求的Path SHA1RequestParas:将请求参数使用SHA-1散列算法加密 使用以上三个参数拼接的Key作为去判断是否重复请求 使用Redis存储Key,

How to use JDBC-Authentication of Spring Boot/Spring Security with Flyway

java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124) at org.springframework.test.context.suppo