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时

时间: 2024-10-14 23:35:12

Spring Boot实战之Filter实现使用JWT进行接口认证的相关文章

《Spring Boot实战》笔记(目录)

目录 目 录第一部分 点睛Spring 4.x第1 章 Spring 基础 ............................................................................................................. 21.1 Spring 概述 .......................................................................................

Spring Boot 实战

原文引用https://www.dazhuanlan.com/2019/08/26/5d630071b1ce1/ 打开google 打开springboot资料 Search or jump to- Pull requests Issues Marketplace Explore @shandudu 59 1,084 316 hansonwang99/Spring-Boot-In-Action Code Issues 0 Pull requests 0 Projects 0 Wiki Secur

spring boot实战(第十五篇)嵌入tomcat源码分析

嵌入tomcat源码分析 在启动spring boot工程时利用@SpringBootApplication注解,该注解启动@EnableAutoConfiguration自动配置,加载META-INF/spring.factories文件 # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.boot.autoconfigure.admin.Spri

spring boot 实战随书源码

spring boot 实战 -汪云飞 随书源码 点击下载

《Spring Boot实战》示例代码问题

* Idea环境 * <Java EE开发的颠覆者 Spring Boot实战> 1.2.2 Spring EL 读取文件获取不到: 需把test.txt和test.properties放在resource文件下,将resource文件标记为资源目录,将引用目录换为"classpath:test.txt"就可以. 2. 2.4  Profile 运行报错:GenericApplicationContext does not support multiple refresh

Spring Boot实战系列(7)集成Consul配置中心

本篇主要介绍了 Spring Boot 如何与 Consul 进行集成,Consul 只是服务注册的一种实现,还有其它的例如 Zookeeper.Etcd 等,服务注册发现在微服务架构中扮演这一个重要的角色,伴随着服务的大量出现,服务与服务之间的配置管理.运维管理也变的难以维护,通过 Consul 可以解决这些问题,实现服务治理.服务监控. 关于 Consul 的更多知识点不在这里赘述,但是在学习本节之前还是希望您能先了解下,请移步我之前写的 微服务服务注册发现之 Consul 系列 快速导航

Spring Boot实战之定制URL匹配规则

本文首发于个人网站:Spring Boot实战之定制URL匹配规则 构建web应用程序时,并不是所有的URL请求都遵循默认的规则.有时,我们希望RESTful URL匹配的时候包含定界符".",这种情况在Spring中可以称之为"定界符定义的格式":有时,我们希望识别斜杠的存在.Spring提供了接口供开发人员按照需求定制. 在之前的几篇文章中,可以通过WebConfiguration类来定制程序中的过滤器.格式化工具等等,同样得,也可以在这个类中用类似的办法配置&

Spring Boot实战之定制type Formatters

本文首发于个人网站:Spring Boot实战之定制type Formatters 前面我们有篇文章介绍了PropertyEditors,是用来将文本类型转换成指定的Java类型,不过,考虑到PropertyEditor的无状态和非线程安全特性,Spring 3增加了一个Formatter接口来替代它.Formatters提供和PropertyEditor类似的功能,但是提供线程安全特性,也可以实现字符串和对象类型的互相转换. 假设在我们的程序中,需要根据一本书的ISBN字符串得到对应的book

Spring Boot 实战:如何自定义 Servlet Filter

1.前言 有些时候我们需要在 Spring Boot Servlet Web 应用中声明一些自定义的 Servlet Filter来处理一些逻辑.比如简单的权限系统.请求头过滤.防止 XSS 攻击等.本篇将讲解如何在 Spring Boot 应用中声明自定义 Servlet Filter 以及定义它们各自的作用域和顺序. 2. 自定义 Filter 可能有人说声明 Servlet Filter 不就是实现 Filter 接口嘛,没有什么好讲的!是的这个没错,但是很多时候我们并不想我们声明的 Fi