详解Spring Boot实战之Filter实现使用JWT进行接口认证

本文介绍了spring Boot实战之Filter实现使用JWT进行接口认证,分享给大家

jwt(json web token)

用户发送按照约定,向服务端发送 Header、Payload 和 Signature,并包含认证信息(密码),验证通过后服务端返回一个token,之后用户使用该token作为登录凭证,适合于移动端和api

jwt使用流程

本文示例接上面几篇文章中的代码进行编写,请阅读本文的同时可以参考前面几篇文章

1、添加依赖库jjwt,本文中构造jwt及解析jwt都使用了jjwt库

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

2、添加登录获取token时,所需要的认证信息类LoginPara.Java

package com.xiaofangtech.sunt.jwt; 

public class LoginPara {
  private String clientId;
  private String userName;
  private String password;
  private String captchaCode;
  private String captchaValue; 

  public String getClientId() {
    return clientId;
  }
  public void setClientId(String clientId) {
    this.clientId = clientId;
  }
  public String getUserName() {
    return userName;
  }
  public void setUserName(String userName) {
    this.userName = userName;
  }
  public String getPassword() {
    return password;
  }
  public void setPassword(String password) {
    this.password = password;
  }
  public String getCaptchaCode() {
    return captchaCode;
  }
  public void setCaptchaCode(String captchaCode) {
    this.captchaCode = captchaCode;
  }
  public String getCaptchaValue() {
    return captchaValue;
  }
  public void setCaptchaValue(String captchaValue) {
    this.captchaValue = captchaValue;
  }
}

3、添加构造jwt及解析jwt的帮助类JwtHelper.java

package com.xiaofangtech.sunt.jwt; 

import java.security.Key;
import java.util.Date; 

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter; 

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm; 

public class JwtHelper {
  public static Claims parseJWT(String jsonWebToken, String base64Security){
    try
    {
      Claims claims = Jwts.parser()
            .setSigningKey(DatatypeConverter.parseBase64Binary(base64Security))
            .parseClaimsJws(jsonWebToken).getBody();
      return claims;
    }
    catch(Exception ex)
    {
      return null;
    }
  } 

  public static String createJWT(String name, String userId, String role,
      String audience, String issuer, long TTLMillis, String base64Security)
  {
    SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; 

    long nowMillis = System.currentTimeMillis();
    Date now = new Date(nowMillis); 

    //生成签名密钥
    byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(base64Security);
    Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName()); 

     //添加构成JWT的参数
    JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT")
                    .claim("role", role)
                    .claim("unique_name", name)
                    .claim("userid", userId)
                    .setIssuer(issuer)
                    .setAudience(audience)
                    .signWith(signatureAlgorithm, signingKey);
     //添加Token过期时间
    if (TTLMillis >= 0) {
      long expMillis = nowMillis + TTLMillis;
      Date exp = new Date(expMillis);
      builder.setExpiration(exp).setNotBefore(now);
    } 

     //生成JWT
    return builder.compact();
  }
}

4、添加token返回结果类AccessToken.java

package com.xiaofangtech.sunt.jwt; 

public class AccessToken {
  private String access_token;
  private String token_type;
  private long expires_in;
  public String getAccess_token() {
    return access_token;
  }
  public void setAccess_token(String access_token) {
    this.access_token = access_token;
  }
  public String getToken_type() {
    return token_type;
  }
  public void setToken_type(String token_type) {
    this.token_type = token_type;
  }
  public long getExpires_in() {
    return expires_in;
  }
  public void setExpires_in(long expires_in) {
    this.expires_in = expires_in;
  }
}

5、添加获取token的接口,通过传入用户认证信息(用户名、密码)进行认证获取

package com.xiaofangtech.sunt.jwt; 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; 

import com.xiaofangtech.sunt.bean.UserInfo;
import com.xiaofangtech.sunt.repository.UserInfoRepository;
import com.xiaofangtech.sunt.utils.MyUtils;
import com.xiaofangtech.sunt.utils.ResultMsg;
import com.xiaofangtech.sunt.utils.ResultStatusCode; 

@RestController
public class JsonWebToken {
  @Autowired
  private UserInfoRepository userRepositoy; 

  @Autowired
  private Audience audienceEntity; 

  @RequestMapping("oauth/token")
  public Object getAccessToken(@RequestBody LoginPara loginPara)
  {
    ResultMsg resultMsg;
    try
    {
      if(loginPara.getClientId() == null
          || (loginPara.getClientId().compareTo(audienceEntity.getClientId()) != 0))
      {
        resultMsg = new ResultMsg(ResultStatusCode.INVALID_CLIENTID.getErrcode(),
            ResultStatusCode.INVALID_CLIENTID.getErrmsg(), null);
        return resultMsg;
      } 

      //验证码校验在后面章节添加 

      //验证用户名密码
      UserInfo user = userRepositoy.findUserInfoByName(loginPara.getUserName());
      if (user == null)
      {
        resultMsg = new ResultMsg(ResultStatusCode.INVALID_PASSWORD.getErrcode(),
            ResultStatusCode.INVALID_PASSWORD.getErrmsg(), null);
        return resultMsg;
      }
      else
      {
        String md5Password = MyUtils.getMD5(loginPara.getPassword()+user.getSalt()); 

        if (md5Password.compareTo(user.getPassword()) != 0)
        {
          resultMsg = new ResultMsg(ResultStatusCode.INVALID_PASSWORD.getErrcode(),
              ResultStatusCode.INVALID_PASSWORD.getErrmsg(), null);
          return resultMsg;
        }
      } 

      //拼装accessToken
      String accessToken = JwtHelper.createJWT(loginPara.getUserName(), String.valueOf(user.getName()),
          user.getRole(), audienceEntity.getClientId(), audienceEntity.getName(),
          audienceEntity.getExpiresSecond() * 1000, audienceEntity.getBase64Secret()); 

      //返回accessToken
      AccessToken accessTokenEntity = new AccessToken();
      accessTokenEntity.setAccess_token(accessToken);
      accessTokenEntity.setExpires_in(audienceEntity.getExpiresSecond());
      accessTokenEntity.setToken_type("bearer");
      resultMsg = new ResultMsg(ResultStatusCode.OK.getErrcode(),
          ResultStatusCode.OK.getErrmsg(), accessTokenEntity);
      return resultMsg; 

    }
    catch(Exception ex)
    {
      resultMsg = new ResultMsg(ResultStatusCode.SYSTEM_ERR.getErrcode(),
          ResultStatusCode.SYSTEM_ERR.getErrmsg(), null);
      return resultMsg;
    }
  }
}

6、添加使用jwt认证的filter

package com.xiaofangtech.sunt.filter; 

import java.io.IOException; 

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.support.SpringBeanAutowiringSupport; 

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xiaofangtech.sunt.jwt.Audience;
import com.xiaofangtech.sunt.jwt.JwtHelper;
import com.xiaofangtech.sunt.utils.ResultMsg;
import com.xiaofangtech.sunt.utils.ResultStatusCode; 

public class HTTPBearerAuthorizeAttribute implements Filter{
  @Autowired
  private Audience audienceEntity; 

  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
    // TODO Auto-generated method stub
    SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
        filterConfig.getServletContext()); 

  } 

  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
    // TODO Auto-generated method stub 

    ResultMsg resultMsg;
    HttpServletRequest httpRequest = (HttpServletRequest)request;
    String auth = httpRequest.getHeader("Authorization");
    if ((auth != null) && (auth.length() > 7))
    {
      String HeadStr = auth.substring(0, 6).toLowerCase();
      if (HeadStr.compareTo("bearer") == 0)
      { 

        auth = auth.substring(7, auth.length());
        if (JwtHelper.parseJWT(auth, audienceEntity.getBase64Secret()) != null)
        {
          chain.doFilter(request, response);
          return;
        }
      }
    } 

    HttpServletResponse httpResponse = (HttpServletResponse) response;
    httpResponse.setCharacterEncoding("UTF-8");
    httpResponse.setContentType("application/json; charset=utf-8");
    httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); 

    ObjectMapper mapper = new ObjectMapper(); 

    resultMsg = new ResultMsg(ResultStatusCode.INVALID_TOKEN.getErrcode(), ResultStatusCode.INVALID_TOKEN.getErrmsg(), null);
    httpResponse.getWriter().write(mapper.writeValueAsString(resultMsg));
    return;
  } 

  @Override
  public void destroy() {
    // TODO Auto-generated method stub 

  }
}

7、在入口处注册filter

package com.xiaofangtech.sunt; 

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

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; 

import com.xiaofangtech.sunt.filter.HTTPBasicAuthorizeAttribute;
import com.xiaofangtech.sunt.filter.HTTPBearerAuthorizeAttribute;
import com.xiaofangtech.sunt.jwt.Audience; 

@SpringBootApplication
@EnableConfigurationProperties(Audience.class)
public class SpringRestApplication { 

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

  @Bean
  public FilterRegistrationBean basicFilterRegistrationBean() {
    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    HTTPBasicAuthorizeAttribute httpBasicFilter = new HTTPBasicAuthorizeAttribute();
    registrationBean.setFilter(httpBasicFilter);
    List<String> urlPatterns = new ArrayList<String>();
    urlPatterns.add("/user/getuser");
    registrationBean.setUrlPatterns(urlPatterns);
    return registrationBean;
  } 

  @Bean
  public FilterRegistrationBean jwtFilterRegistrationBean(){
    FilterRegistrationBean registrationBean = new FilterRegistrationBean();
    HTTPBearerAuthorizeAttribute httpBearerFilter = new HTTPBearerAuthorizeAttribute();
    registrationBean.setFilter(httpBearerFilter);
    List<String> urlPatterns = new ArrayList<String>();
    urlPatterns.add("/user/getusers");
    registrationBean.setUrlPatterns(urlPatterns);
    return registrationBean;
  }
}

8、添加获取md5的方法类MyUtils

package com.xiaofangtech.sunt.utils; 

import java.security.MessageDigest; 

public class MyUtils {
  public static String getMD5(String inStr) {
    MessageDigest md5 = null;
    try {
      md5 = MessageDigest.getInstance("MD5");
    } catch (Exception e) { 

      e.printStackTrace();
      return "";
    }
    char[] charArray = inStr.toCharArray();
    byte[] byteArray = new byte[charArray.length]; 

    for (int i = 0; i < charArray.length; i++)
      byteArray[i] = (byte) charArray[i]; 

    byte[] md5Bytes = md5.digest(byteArray); 

    StringBuffer hexValue = new StringBuffer(); 

    for (int i = 0; i < md5Bytes.length; i++) {
      int val = ((int) md5Bytes[i]) & 0xff;
      if (val < 16)
        hexValue.append("0");
      hexValue.append(Integer.toHexString(val));
    } 

    return hexValue.toString();
  }
}

9、在返回信息类中补充添加错误码

INVALID_CLIENTID(30003, "Invalid clientid"),
INVALID_PASSWORD(30004, "User name or password is incorrect"),
INVALID_CAPTCHA(30005, "Invalid captcha or captcha overdue"),
INVALID_TOKEN(30006, "Invalid token"); 

10、代码中涉及的Audience类,在上一篇文章中定义,本文不再重复说明

11、代码整体结构

12、测试

1) 获取token,传入用户认证信息

认证通过返回token信息

2) 使用上面获取的token进行接口调用

未使用token,获取token错误,或者token过期时

使用正确的token时

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Boot 实例代码之通过接口安全退出

    1.在pom.xml中引入actuator, security依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot&l

  • 详解Spring Boot实战之Rest接口开发及数据库基本操作

    本文介绍了Spring Boot实战之Rest接口开发及数据库基本操作,分享给大家 1.修改pom.xml,添加依赖库,本文使用的是mysql <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <

  • 浅谈spring-boot 允许接口跨域并实现拦截(CORS)

    本文介绍了spring-boot 允许接口跨域并实现拦截(CORS),分享给大家,也给自己留个笔记 pom.xml(依赖的jar) // 在spring-boot-starter-web的启动器中,已经依赖好了 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependen

  • 详解Spring Boot实战之Filter实现使用JWT进行接口认证

    本文介绍了spring Boot实战之Filter实现使用JWT进行接口认证,分享给大家 jwt(json web token) 用户发送按照约定,向服务端发送 Header.Payload 和 Signature,并包含认证信息(密码),验证通过后服务端返回一个token,之后用户使用该token作为登录凭证,适合于移动端和api jwt使用流程 本文示例接上面几篇文章中的代码进行编写,请阅读本文的同时可以参考前面几篇文章 1.添加依赖库jjwt,本文中构造jwt及解析jwt都使用了jjwt库

  • 实例详解Spring Boot实战之Redis缓存登录验证码

    本章简单介绍redis的配置及使用方法,本文示例代码在前面代码的基础上进行修改添加,实现了使用redis进行缓存验证码,以及校验验证码的过程. 1.添加依赖库(添加redis库,以及第三方的验证码库) <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> </dependency&

  • 详解Spring Boot实战之单元测试

    本文介绍使用Spring测试框架提供的MockMvc对象,对Restful API进行单元测试 Spring测试框架提供MockMvc对象,可以在不需要客户端-服务端请求的情况下进行MVC测试,完全在服务端这边就可以执行Controller的请求,跟启动了测试服务器一样. 测试开始之前需要建立测试环境,setup方法被@Before修饰.通过MockMvcBuilders工具,使用WebApplicationContext对象作为参数,创建一个MockMvc对象. MockMvc对象提供一组工具

  • 详解Spring Boot实战之Restful API的构建

    上一篇文章讲解了通过Spring boot与JdbcTemplate.JPA和MyBatis的集成,实现对数据库的访问.今天主要给大家分享一下如何通过Spring boot向前端返回数据. 在现在的开发流程中,为了最大程度实现前后端的分离,通常后端接口只提供数据接口,由前端通过Ajax请求从后端获取数据并进行渲染再展示给用户.我们用的最多的方式就是后端会返回给前端一个JSON字符串,前端解析JSON字符串生成JavaScript的对象,然后再做处理.本文就来演示一下Spring boot如何实现

  • 详解spring boot rest例子

    简介:本文将帮助您使用 Spring Boot 创建简单的 REST 服务. 你将学习 什么是 REST 服务? 如何使用 Spring Initializr 引导创建 Rest 服务应用程序? 如何创建获取 REST 服务以检索学生注册的课程? 如何为学生注册课程创建 Post REST 服务? 如何利用 postman 执行 rest 服务? 本教程使用的 rest 服务 在本教程中,我们将使用适当的 URI 和 HTTP 方法创建三个服务: @GetMapping("/ students

  • 详解spring boot starter redis配置文件

    spring-boot-starter-Redis主要是通过配置RedisConnectionFactory中的相关参数去实现连接redis service. RedisConnectionFactory是一个接口,有如下4个具体的实现类,我们通常使用的是JedisConnectionFactory. 在spring boot的配置文件中redis的基本配置如下: # Redis服务器地址 spring.redis.host=192.168.0.58 # Redis服务器连接端口 spring.

  • 详解Spring boot Admin 使用eureka监控服务

    前言 最近刚好有空,来学习一下如何搭建spring boot admin环境.其中遇到很多的坑. 网上大多都是使用admin-url的方式直接来监控的,感觉一点也不灵活,这不是我想要的结果,所以本篇介绍借助eureka服务注册和发现功能来灵活监控程序. 本文主要记录spring boot admin的搭建过程,希望能有所帮助.其实非常的简单,不要被使用常规方式的误导! 环境介绍 IDE:intellij idea jdk: java8 maven:3.3.9 spring boot:1.5.6

  • 详解spring boot jpa整合QueryDSL来简化复杂操作

    前言 使用过spring data jpa的同学,都很清楚,对于复杂的sql查询,处理起来还是比较复杂的,而本文中的QueryDSL就是用来简化JPA操作的. Querydsl定义了一种常用的静态类型语法,用于在持久域模型数据之上进行查询.JDO和JPA是Querydsl的主要集成技术.本文旨在介绍如何使用Querydsl与JPA组合使用.JPA的Querydsl是JPQL和Criteria查询的替代方法.QueryDSL仅仅是一个通用的查询框架,专注于通过Java API构建类型安全的SQL查

  • 详解Spring Boot中使用Flyway来管理数据库版本

    如果没有读过上面内容的读者,有兴趣的可以一阅.在上面的使用JdbcTemplate一文中,主要通过spring提供的JdbcTemplate实现对用户表的增删改查操作.在实现这个例子的时候,我们事先在MySQL中创建了用户表.创建表的过程我们在实际开发系统的时候会经常使用,但是一直有一个问题存在,由于一个系统的程序版本通过git得到了很好的版本控制,而数据库结构并没有,即使我们通过Git进行了语句的版本化,那么在各个环境的数据库中如何做好版本管理呢?下面我们就通过本文来学习一下在Spring B

随机推荐