springBoot项目 项目集成JWT使用token身份验证

RESTful API认证方式 大致有以下4种

  1. Basic Authentication
    HTTP Basic authentication is described in RFC 2617. It’s a simple username/password scheme. 将用户名与密码进行Base64转码,但这种转码是可逆的。某些爬虫工具可能会获取这些请求信息,直接获取用户的账号和密码,如果采用HTTPS方式发送请求,每次请求和响应会被SSL加密,爬虫无法获取这些信息。另一个问题,由于API通常不能信任用户使用的客户端,如果用户在多个设备(平板、电脑、手机)中登录了这个API服务,其中一个设备出现安全问题,需要修改密码,那么其他设备也需要重新登录才行。为了解决第二个问题,需要对每个设备给予不同的证书。

  2. OAuth
    OAuth 是一种授权框架,能够让应用通过HTTP 服务获取有限的访问,访问用户账号信息,例如Facebook, GitHub, DigitalOcean都采用该技术。它可以委托认证服务授权第三方应用访问自己的账号信息。OAuth2 相比OAuth 1,可以在PC端、移动端设备上使用。
    OAuth 定义了四种角色:
    1)资源所属者,User, 拥有该资源的人,拥有Application所访问资源的权限。
    2)客户端, Application, 需要访问用户账号信息的应用
    3)资源服务器, API
    4)授权服务器, API

  3. Token Authentication
    JWT( JSON Web Token), 是一种以Base64编码json对象的token,加密,紧凑且自成一体(self-contained),用于在网络中两个节点之间传递信息。开源标准(RFC 7519)
    JWT由三个部分组成:
    1)Header, 定义加密算法类型(例如:HS256)、定义类型(JWT)
    2)Payload, 定义token携带的主要信息
    3)Signature, 创建token的签名,

  4. OpenID
    OpenID是一个去中心化的网上身份认证系统。对于支持OpenID的网站,用户不需要记住像用户名和密码这样的传统验证标记。取而代之的是,他们只需要预先在一个作为OpenID身份提供者(identity provider, IdP)的网站上注册。OpenID是去中心化的,任何网站都可以使用OpenID来作为用户登录的一种方式,任何网站也都可以作为OpenID身份提供者。OpenID既解决了问题而又不需要依赖于中心性的网站来确认数字身份。

我们使用JWT不仅仅是因为它简单易用,更有着很多优点:
1.简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快
2.自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库
3.因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持。
4.不需要在服务端保存会话信息,特别适用于分布式微服务。

下面上代码,pringBoot集成JWT进行登录的身份验证

首先要吧JWT的maven坐标引入项目的pom文件中

   	<dependency>
   		<groupId>com.auth0</groupId>
   		<artifactId>java-jwt</artifactId>
   		<version>3.4.0</version>
   	</dependency>

我自己写个TokenService,里面只有一个生成token的方法

package com.example.demo.service.impl;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.example.demo.model.User;
import org.springframework.stereotype.Service;

@Service
public class TokenService {

	public String getToken(User user) {
		// token的生成方法
		// Algorithm.HMAC256():使用HS256生成token,密钥则是用户的密码,唯一密钥的话可以保存在服务端。
		// withAudience()存入需要保存在token的信息,这里我把用户ID存入token中
		String token = "";
		token = JWT.create().withAudience(user.getId().toString()).sign(Algorithm.HMAC256(user.getPassword()));
		return token;

	}
}

接着写个自定义注解,标注在方法上用来进行生成token和验证token等操作

package com.example.demo.util;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* @Target:注解的作用目标
* @Target(ElementType.TYPE)——接口、类、枚举、注解
* @Target(ElementType.FIELD)——字段、枚举的常量
* @Target(ElementType.METHOD)——方法
* @Target(ElementType.PARAMETER)——方法参数
* @Target(ElementType.CONSTRUCTOR) ——构造函数
* @Target(ElementType.LOCAL_VARIABLE)——局部变量
* @Target(ElementType.ANNOTATION_TYPE)——注解
* @Target(ElementType.PACKAGE)——包
* 
* @Retention:注解的保留位置 RetentionPolicy.SOURCE:这种类型的Annotations只在源代码级别保留,编译时就会被忽略,在class字节码文件中不包含。
*                    RetentionPolicy.CLASS:这种类型的Annotations编译时被保留,默认的保留策略,在class文件中存在,但JVM将会忽略,运行时无法获得。
*                    RetentionPolicy.RUNTIME:这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。
* @Document:说明该注解将被包含在javadoc中
* @Inherited:说明子类可以继承父类中的该注解
* 
* 
* @author onegis
*
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {

   boolean required() default true;
}

然后自定义一个拦截器,拦截所有方法,判断是否拥有上一步的注解,有则生成,验证token

package com.example.demo.interceptor;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import com.example.demo.util.PassToken;
import com.example.demo.util.UserLoginToken;
/**
* 自定义的拦截器
* @author onegis
*
*/
public class AuthenticationInterceptor implements HandlerInterceptor{

   @Autowired
   UserService userService;
   
   
   @Override
   public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object)
   		throws Exception {
   	 
   	 String token = request.getHeader("token");
   	
   	 // 如果不是映射到方法直接通过
   	if(!(object instanceof HandlerMethod)){
   		return true;
   	}
   	HandlerMethod handlerMethod=(HandlerMethod)object;
       Method method = handlerMethod.getMethod();
   	
     
   	
   	//检查有没有需要用户权限的注解 @UserLoginToken
       if(method.isAnnotationPresent(UserLoginToken.class)){
       	UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
           if(userLoginToken.required()){
           	//开始认证
           	if(token==null){
           		throw new RuntimeException("没有token,请重新登录!");
           	}
           //拿到token中的userId
           	String userId;
           	try {
           		userId = JWT.decode(token).getAudience().get(0);
   			} catch (Exception e) {
   				 throw new RuntimeException("401");
   			}
           	
           	User user = userService.findUserById(Integer.parseInt(userId));
           	
           	if(user==null){
           		throw new RuntimeException("用户不存在,请重新登录");
           	}
           	
           	//验证token
           	 JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
           	try {
           		 jwtVerifier.verify(token);
   			} catch (Exception e) {
   			    throw new RuntimeException("401");
   			}
           	
           	 return true;
           }
       	
       }

    	return true;
   }
   
}

将自己写的拦截器注入到拦截器配置中

package com.example.demo.interceptor;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 拦截器配置
* 将自定义的拦截器注入到 配置中
* @author onegis
*
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer  {
   @Override
   public void addInterceptors(InterceptorRegistry registry) {
    //拦截所有请求,判断是否有@UserLoginToken注解,决定是否需要验证
   	 registry.addInterceptor(authenticationInterceptor()).addPathPatterns("/*/*");
   	 
   }
   @Bean
   public AuthenticationInterceptor authenticationInterceptor(){
   	return new AuthenticationInterceptor();
   }

}

最后就是在controller层调用了

@RequestMapping("/login")
	@ResponseBody
	public Object login(@RequestBody User user)  {
		 JSONObject jsonObject = new JSONObject();
		User userForBase = userService.findUserByUserName(user);
		if (userForBase == null) {

			jsonObject.put("message", "登录失败,用户不存在");

			return jsonObject;

		} else {

			if (!userForBase.getPassword().equals(user.getPassword())) {

				jsonObject.put("message", "密码错误!");

				return jsonObject;

			} else {
				String token = tokenService.getToken(userForBase);

				jsonObject.put("token", token);
				jsonObject.put("user", userForBase);

				return jsonObject;

			}

		}

	}
	@ResponseBody
	@UserLoginToken
	@RequestMapping("/getMessage")
	public String getMessage() {
		return "通过验证!";

	}

如果直接调用getMessage 接口的话

在这里插入图片描述
拦截到没有token,要求重新登录
我们调用Login接口,生成token
在这里插入图片描述
接着把token记下来,访问getMessage接口的时候,放到header中
在这里插入图片描述
至此结束

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值