JWT原理

一、什么是JWT

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).

该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。

JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yiCp0u0w-1654620729476)(https://gitee.com/maidang-zc/image/raw/master/img/fc1f4134970a304ea501a2f40d06848fc8175cf0.png)]

二、JWT的组成

1、JWT生成编码后的样子

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q

2、JWT由三部分构成

第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).

header

jwt的头部承载两部分信息:

声明类型,这里是jwt声明加密的算法 通常直接使用 HMAC SHA256完整的头部就像下面这样的JSON:

{“typ”:“JWT”,“alg”:“HS256”}

然后将头部进行base64加密(该加密是可以对称解密的),构成了第一部分

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

playload

载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分

标准中注册的声明公共的声明私有的声明标准中注册的声明 (建议但不强制使用) :

iss: jwt签发者

sub: jwt所面向的用户

aud: 接收jwt的一方

exp: jwt的过期时间,这个过期时间必须要大于签发时间

nbf: 定义在什么时间之前,该jwt都是不可用的.

iat: jwt的签发时间

jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

公共的声明 :

公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.

私有的声明 :

私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。

定义一个payload:

{“iss”:“http://127.0.0.1:8000/api/login”,“iat”:1641040511,“exp”:1641044111,“nbf”:1641040511,“jti”:“wwT82LjYfybCScvL”,“sub”:3,“prv”:“23bd5c8949f600adb39e701c400872db7a5976f7”}

然后将其进行base64加密,得到Jwt的第二部分

eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjgwMDAvYXBpL2xvZ2luIiwiaWF0IjoxNjQxMDQwNTExLCJleHAiOjE2NDEwNDQxMTEsIm5iZiI6MTY0MTA0MDUxMSwianRpIjoid3dUODJMallmeWJDU2N2TCIsInN1YiI6MywicHJ2IjoiMjNiZDVjODk0OWY2MDBhZGIzOWU3MDFjNDAwODcyZGI3YTU5NzZmNyJ9

signature

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

header (base64后的)

payload (base64后的)

secret这个部分需要base64加密后的header和base64加密后的payload使用

连接组成的字符串(头部在前),然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),密钥
)

UQmqAUhUrpDVV2ST7mZKyLTomVfg7sYkEjmdDI5XF8Q

密钥secret是保存在服务端的,服务端会根据这个密钥进行生成token和验证,所以需要保护好。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JSvgI7FY-1654620729478)(https://gitee.com/maidang-zc/image/raw/master/img/71cf3bc79f3df8dcf8062d8f12df5182461028c8.png)]

3、签名的目的

最后一步签名的过程,实际上是对头部以及载荷内容进行签名。一般而言,加密算法对于不同的输入产生的输出总是不一样的。对于两个不同的输入,产生同样的输出的概率极其地小(有可能比我成世界首富的概率还小)。所以,我们就把“不一样的输入产生不一样的输出”当做必然事件来看待吧。

所以,如果有人对头部以及载荷的内容解码之后进行修改,再进行编码的话,那么新的头部和载荷的签名和之前的签名就将是不一样的。而且,如果不知道服务器加密的时候用的密钥的话,得出来的签名也一定会是不一样的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W5jSkRjl-1654620729479)(https://gitee.com/maidang-zc/image/raw/master/img/2934349b033b5bb55554ae46ea1df630b400bcdb.png)]

服务器应用在接受到JWT后,会首先对头部和载荷的内容用同一算法再次签名。那么服务器应用是怎么知道我们用的是哪一种算法呢?别忘了,我们在JWT的头部中已经用alg字段指明了我们的加密算法了。

如果服务器应用对头部和载荷再次以同样方法签名之后发现,自己计算出来的签名和接受到的签名不一样,那么就说明这个Token的内容被别人动过的,我们应该拒绝这个Token,返回一个HTTP 401 Unauthorized响应。

注意:在JWT中,不应该在载荷里面加入任何敏感的数据,比如用户的密码。

4、如何应用

一般是在请求头里加入Authorization,并加上Bearer标注:

fetch(‘api/user/1’, { headers: {‘Authorization’: 'Bearer ’ + token }})服务端会验证token,如果验证通过就会返回相应的资源。

5、安全相关

不应该在jwt的payload部分存放敏感信息,因为该部分是客户端可解密的部分。保护好secret私钥,该私钥非常重要。如果可以,请使用https协议6、对Token认证的五点认识

一个Token就是一些信息的集合;在Token中包含足够多的信息,以便在后续请求中减少查询数据库的几率;服务端需要对cookie和HTTP Authrorization Header进行Token信息的检查;基于上一点,你可以用一套token认证代码来面对浏览器类客户端和非浏览器类客户端;因为token是被签名的,所以我们可以认为一个可以解码认证通过的token是由我们系统发放的,其中带的信息是合法有效的;三、传统的session认证

我们知道,http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据http协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证。

但是这种基于session的认证使应用本身很难得到扩展,随着不同客户端用户的增加,独立的服务器已无法承载更多的用户,而这时候基于session认证应用的问题就会暴露出来。

基于session认证所显露的问题

**Session:**每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。

**扩展性:**用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。

**CSRF:**因为是基于cookie来进行用户识别的, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。

基于token的鉴权机制

基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。

流程上是这样的:

用户使用用户名密码来请求服务器服务器进行验证用户的信息服务器通过验证发送给用户一个token客户端存储token,并在每次请求时附送上这个token值服务端验证token值,并返回数据这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了 Access-Control-Allow-Origin:*。

四、token的优点

支持跨域访问: Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输。无状态(也称:服务端可扩展行):Token机制在服务端不需要存储session信息,因为Token 自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息。更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等),而你的服务端只要提供API即可。去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可。更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android,Windows 8等)时,Cookie是不被支持的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范。性能: 一次网络往返时间(通过数据库查询session信息)总比做一次HMACSHA256计算 的Token验证和解析要费时得多。不需要为登录页面做特殊处理: 如果你使用Protractor 做功能测试的时候,不再需要为登录页面做特殊处理。基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft)。因为json的通用性,所以JWT是可以进行跨语言支持的,像JAVA,JavaScript,NodeJS,PHP等很多语言都可以使用。因为有了payload部分,所以JWT可以在自身存储一些其他业务逻辑所必要的非敏感信息。便于传输,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。它不需要在服务端保存会话信息, 所以它易于应用的扩展。五、JWT的JAVA实现

Java中对JWT的支持可以考虑使用JJWT开源库;JJWT实现了JWT, JWS, JWE 和 JWA RFC规范;

下面将简单举例说明其使用:

1、生成Token码

import javax.crypto.spec.SecretKeySpec;import javax.xml.bind.DatatypeConverter;import java.security.Key;import io.jsonwebtoken.*;import java.util.Date; //Sample method to construct a JWTprivate String createJWT(String id, String issuer, String subject, long ttlMillis){//The JWT signature algorithm we will be using to sign the tokenSignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);//We will sign our JWT with our ApiKey secretbyte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(apiKey.getSecret());Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());//Let’s set the JWT ClaimsJwtBuilder builder = Jwts.builder().setId(id) .setIssuedAt(now) .setSubject(subject) .setIssuer(issuer) .signWith(signatureAlgorithm, signingKey);//if it has been specified, let’s add the expirationif (ttlMillis >= 0) {long expMillis = nowMillis + ttlMillis; Date exp = new Date(expMillis); builder.setExpiration(exp);}//Builds the JWT and serializes it to a compact, URL-safe stringreturn builder.compact();}
2、解码和验证Token码

import javax.xml.bind.DatatypeConverter;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.Claims;//Sample method to validate and read the JWTprivatevoidparseJWT(String jwt){//This line will throw an exception if it is not a signed JWS (as expected)Claims claims = Jwts.parser() .setSigningKey(DatatypeConverter.parseBase64Binary(apiKey.getSecret())) .parseClaimsJws(jwt).getBody();System.out.println("ID: " + claims.getId());System.out.println("Subject: " + claims.getSubject());System.out.println("Issuer: " + claims.getIssuer());System.out.println("Expiration: " + claims.getExpiration());}

文章整理自:

https://blog.csdn.net/buyaoshuohua1/article/details/73739419

https://www.cnblogs.com/xiekeli/p/5607107.html#top

https://blog.csdn.net/SoftwareOscar/article/details/78538346

https://blog.csdn.net/Jack__Frost/article/details/64964208

http://blog.leapoahead.com/2015/09/06/understanding-jwt/

创建新的项目

通过运行下面的命令,我们就可以开始并创建新的 Laravel 项目。

composer create-project --prefer-dist laravel/laravel lara-jwtapp

这会在名为 jwt 的目录下创建一个新的 Laravel 项目。

配置 JWT 扩展包

我们会使用 tymondesigns/jwt-auth 扩展包来让我们在 Laravel 中使用 JWT。

安装 tymon/jwt-auth 扩展包

让我们在这个 Laravel 应用中安装这个扩展包。如果您正在使用 Laravel 5.5 或以上版本,请运行以下命令来获取 dev-develop 版本的 JWT 包:

composer require tymon/jwt-auth:dev-develop --prefer-source

发布配置文件

对于 5.5 或以上版本 的 Laravel,请使用下面这条命令来发布配置文件:

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

上面的命令会生成 config/jwt.php 配置文件。除去注释部分,配置文件会像这样:

<?phpreturn ['secret' => env('JWT_SECRET'),'keys' => ['public' => env('JWT_PUBLIC_KEY'),'private' => env('JWT_PRIVATE_KEY'),'passphrase' => env('JWT_PASSPHRASE'),],'ttl' => env('JWT_TTL', 60),'refresh_ttl' => env('JWT_REFRESH_TTL', 20160),'algo' => env('JWT_ALGO', 'HS256'),'required_claims' => ['iss','iat','exp','nbf','sub','jti',],'persistent_claims' => [// 'foo',// 'bar',],'lock_subject' => true,'leeway' => env('JWT_LEEWAY', 0),'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0),'decrypt_cookies' => false,'providers' => ['jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class,'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class,'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class,],
];

生成 JWT 密钥

JWT 令牌通过一个加密的密钥来签发。对于 Laravel 5.5 或以上版本,运行下面的命令来生成密钥以便用于签发令牌。

php artisan jwt:secret

注册中间件

JWT 认证扩展包附带了允许我们使用的中间件。在 app/Http/Kernel.php 中注册 auth.jwt 中间件:

protected $routeMiddleware = [....'auth.jwt' => \Tymon\JWTAuth\Http\Middleware\Authenticate::class,
];

这个中间件会通过检查请求中附带的令牌来校验用户的认证。如果用户未认证,这个中间件会抛出 UnauthorizedHttpException 异常。

设置路由

开始之前,我们将为所有本教程讨论的点设置路由。打开 routes/api.php 并将下面的路由复制到您的文件中。

Route::post('login',[ApiController::class,'login']);
Route::post('register',[ApiController::class,'register']);
Route::get('verify',[ApiController::class,'verify']);
Route::get('resetemail',[ApiController::class,'resetemail']);
Route::post('resetpassword',[ApiController::class,'reset']);Route::group(['middleware' => 'auth.jwt'], function () {Route::get('logout', [ApiController::class, 'logout']);Route::get('user', [ApiController::class, 'getAuthUser']);Route::get('products', [ProductController::class, 'index']);Route::get('products/{id}', [ProductController::class, 'show']);Route::post('products', [ProductController::class, 'store']);Route::put('products/{id}', [ProductController::class, 'update']);Route::delete('products/{id}', [ProductController::class, 'destroy']);
});

更新 User 模型

JWT 需要在 User 模型中实现 TymonJWTAuthContractsJWTSubject 接口。 此接口需要实现两个方法 getJWTIdentifiergetJWTCustomClaims。使用以下内容更新 app/User.php

<?phpnamespace App;use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject;class User extends Authenticatable implements JWTSubject
{use Notifiable;protected $fillable = ['name', 'email', 'password','verification',];protected $hidden = ['password', 'remember_token',];public function getJWTIdentifier(){return $this->getKey();}public function getJWTCustomClaims(){return [];}
}

JWT 身份验证逻辑

让我们使用 JWT 身份验证在 laravel 中写 Restful API 的逻辑。

用户注册时需要姓名,邮箱和密码。那么,让我们创建一个表单请求来验证数据。通过运行以下命令创建名为 RegisterAuthRequest 的表单请求:

php artisan make:request RegisterAuthRequest

它将在 app/Http/Requests 目录下创建 RegisterAuthRequest.php 文件。将下面的代码黏贴至该文件中。

<?phpnamespace App\Http\Requests;use Illuminate\Foundation\Http\FormRequest;class RegisterAuthRequest extends FormRequest
{public function authorize(){return true;}public function rules(){return ['name' => 'required|string','email' => 'required|email|unique:users','password' => 'required|string|min:6|max:10'];}
}

创建ApiController

运行以下命令创建一个新的 ApiController :

php artisan make:controller ApiController

这将会在 app/Http/Controllers 目录下创建 ApiController.php 文件。将下面的代码黏贴至该文件中。

<?phpnamespace App\Http\Controllers;use Illuminate\Http\Request;
use App\Http\Requests\RegisterAuthRequest;
use App\Models\User;
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
use Illuminate\Support\Facades\Mail;class ApiController extends Controller
{public $loginAfterSignUp = false;public function register(RegisterAuthRequest $request){$user = new User();$user->name = $request->name;$user->email = $request->email;$user->password = bcrypt($request->password);$user->verification = 0;$user->save();if ($this->loginAfterSignUp) {return $this->login($request);}$secret='http://127.0.0.1:8000/api/verify?verification='.$this->generate_verification($request->email);$to = $request->email;Mail::send('emails.verification',['name'=> $request->name,'link'=>$secret],function($message) use ($to){$message ->to($to)->subject('激活邮件');});return response()->json(['success' => true,'data' => $user], 200);}public function verify(Request $request){$this->validate($request,['verification'=>'required']);$str = $request->verification;$ret = $this->check_verification($str);if($ret['code'] == 200){$user = new User();$user->where('email',$ret['email'])->update(['verification' => 1]);return response()->json(['success' =>true,'message' => 'Success!'],200);}else{return response()->json(['success' =>false,'message' =>'Sorry,activate failed!','ret' =>$ret,],500);}}public function login(Request $request){$input = $request->only('email', 'password');$jwt_token = null;$user = new User();$user = $user->firstWhere('email',$request->email);if ($user==null) {return response()->json(['success' => false,'message' => 'Please register your email!',],400);}if ($user->verification === 0){return response()->json(['success' => false,'message' => 'Please verify your account!'],401);}if (!$jwt_token = JWTAuth::attempt($input)) {return response()->json(['success' => false,'message' => 'Invalid Email or Password',], 401);}return response()->json(['success' => true,'token' => $jwt_token,]);}public function logout(Request $request){$this->validate($request, ['token' => 'required']);try {JWTAuth::invalidate($request->token);return response()->json(['success' => true,'message' => 'User logged out successfully']);} catch (JWTException $exception) {return response()->json(['success' => false,'message' => 'Sorry, the user cannot be logged out'], 500);}}public function getAuthUser(Request $request){$this->validate($request, ['token' => 'required']);$user = JWTAuth::authenticate($request->token);return response()->json(['user' => $user]);}public function generate_verification($email){//用户名、此时的时间戳,并将过期时间拼接在一起$time = time();//设置token过期时间为一小时$end_time = time()+3600;$info =$this->passport_encrypt( $email. '.' .$time.'.'.$end_time);//根据以上信息信息生成签名$signature = hash_hmac('md5',$info,'maidang123456');//最后将这两部分拼接起来,得到最终的Token字符串return $info . '.' . $signature;}public function check_verification($token){/**** api传来的token ****/if(!isset($token) || empty($token)){$ret['code']='400';$ret['ret']='非法请求';return $ret;}//对比token$explode = explode('.',$token);//以.分割token为数组$encodeinfo=urlencode($explode[0]);$true_signature = hash_hmac('md5',$encodeinfo,'maidang123456');$data = $this->passport_decrypt($explode[0]).'.'.$explode[1];$explode = explode('.',$data);if(!empty($explode[0]) && !empty($explode[1]) && !empty($explode[2]) && !empty($explode[3]) && !empty($explode[4]) ){if(time() > $explode[3]){$ret['code']='401';$ret['ret']='Token已过期,请重新登录';return $ret;}if ($true_signature == $explode[4]){$ret['code']='200';$ret['ret']='Token合法';$ret['email'] = $explode[0].'.'.$explode[1];return $ret;}else{$ret['code']='400';$ret['ret']='Token不合法';return $ret;}}else{$ret['code']='400';$ret['ret']='Token不合法';return $ret;}}public function passport_encrypt($txt, $key = 'maidang123456.hdu'){$encrypt_key = md5(rand(1000000,9999999));$ctr = 0;$tmp = '';for ($i = 0; $i < strlen($txt); $i++) {$ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;$tmp .= $encrypt_key[$ctr] . ($txt[$i] ^ $encrypt_key[$ctr++]);}return urlencode(base64_encode($this->passport_key($tmp, $key)));}public function passport_decrypt($txt, $key = 'maidang123456.hdu'){$txt = $this->passport_key(base64_decode($txt), $key);$tmp = '';for ($i = 0; $i < strlen($txt); $i++) {$md5 = $txt[$i];$tmp .= $txt[++$i] ^ $md5;}return $tmp;}protected function passport_key($txt, $encrypt_key){$encrypt_key = md5($encrypt_key);$ctr = 0;$tmp = '';for ($i = 0; $i < strlen($txt); $i++) {$ctr = $ctr == strlen($encrypt_key) ? 0 : $ctr;$tmp .= $txt[$i] ^ $encrypt_key[$ctr++];}return $tmp;}public function resetemail(Request $request){$this->validate($request,['email' => 'required']);if (User::where('email', '=', $request->email)->exists()==null) {return response()->json(['success' => false,'message' => 'Please register your email!',],400);}$to = $request->email;$secret = $secret = 'http://127.0.0.1:8000/api/resetpassword?RESETMESG='.$this->generate_verification($request->email);Mail::send('emails.reset',['link' =>$secret],function($message)use ($to){$message ->to($to)->subject('重置密码邮件');});return response()->json(['success' => true,'message' => '重置密码邮件已发送到你的邮箱',],400);}public function  reset(Request $request){$this->validate($request,['RESETMESG' => 'required','password1'  => 'required','password2' => 'required']);if($request->password1 != $request->password2){return response()->json(['success' => false,'message' => '两次输入密码不一致!'],400);}$str = $request->RESETMESG;$ret = $this->check_verification($str);if ($ret['code'] == 200) {$user = new User();$user->where('email',$ret['email'])->update(['password' => bcrypt($request->password1)]);return response()->json(['success' =>true,'message' => 'Reset success!'],200);}else{return response()->json(['success' =>false,'message' =>'Sorry,reset failed!','ret' =>$ret,],500);}}
}

register 方法中,我们接收了 RegisterAuthRequest 。使用请求中的数据创建用户,初始化verification值为0。如果 loginAfterSignUp 属性为 true ,则注册后通过调用 login 方法为用户登录。使用generate_verification生成token信息,发送邮箱验证,用户点击链接激活账号。注册成功,响应则将用户数据一起返回。

verify方法中,验证了返回值的有效性,使用check_verification方法验证token是否一致,确认一致后,修改用户verification值为1,激活成功。

login 方法中,我们得到了请求的子集,其中只包含电子邮件和密码。以输入的值作为参数调用 JWTAuth::attempt() ,响应保存在一个变量中。如果从 attempt 方法中返回 false ,则返回一个失败响应。否则,将返回一个成功的响应。

logout 方法中,验证请求是否包含令牌验证。通过调用 invalidate 方法使令牌无效,并返回一个成功的响应。如果捕获到 JWTException 异常,则返回一个失败的响应。

getAuthUser 方法中,验证请求是否包含令牌字段。然后调用 authenticate 方法,该方法返回经过身份验证的用户。最后,返回带有用户的响应。

resetemail方法中,验证邮箱存在后,发送重置密码的邮件,收到邮件后调用reset方法,修改用户密码(由于未设计前端页面,所以需要手动传参)

passport_encryptpassport_decryptpassport_key是网上找的php的加解密函数和密钥生成函数,用于生成验证token过程中。

身份验证部分现在已经完成。

构建产品部分

要创建产品部分,我们需要 Product 模型,控制器和迁移文件。运行以下命令来创建 Product 模型,控制器和迁移文件。

php artisan make:model Product -mc

它会在 database/migrations 目录下创建一个新的数据库迁移文件 create_products_table.php,更改 up 方法。

public function up()
{Schema::create('products', function (Blueprint $table) {$table->increments('id');$table->integer('user_id');$table->string('name');$table->integer('price');$table->integer('quantity');$table->timestamps();$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');});
}

Product 模型中添加 fillable 属性。在 app 目录下打开 Product.php 文件并添加属性。

protected $fillable = ['name', 'price', 'quantity'
];

现在在 .env 文件中设置数据库凭证,并通过运行以下命令迁移数据库。

php artisan migrate

现在,我们必须在 User 模型中添加一个关系来检索相关产品。在 app/User.php 中添加以下方法。

public function products()
{return $this->hasMany(Product::class);
}

app/Http/Controllers 目录下打开 ProductController.php 文件。在文件开头添加 use 指令覆盖上一个。

use App\Product;
use Illuminate\Http\Request;
use JWTAuth;

现在我们将实现五个方法。

  • index, 为经过身份认证的用户获取所有产品列表
  • show, 根据 ID 获取特定的产品
  • store, 将新产品存储到产品列表中
  • update, 根据 ID 更新产品详情
  • destroy, 根据 ID 从列表中删除产品

添加一个构造函数来获取经过身份认证的用户,并将其保存在 user 属性中。

protected $user;public function __construct()
{$this->user = JWTAuth::parseToken()->authenticate();
}

parseToken 将解析来自请求的令牌, authenticate 通过令牌对用户进行身份验证。

让我们添加 index 方法。

public function index()
{return $this->user->products()->get(['name', 'price', 'quantity'])->toArray();
}

上面的代码非常简单,我们只是使用 Eloquent 的方法获取所有的产品,然后将结果组成一个数组。最后,我们返回这个数组。Laravel 将自动将其转换为 JSON ,并创建一个为 200 成功的响应码。

继续实现 show 方法。

public function show($id)
{$product = $this->user->products()->find($id);if (!$product) {return response()->json(['success' => false,'message' => 'Sorry, product with id ' . $id . ' cannot be found'], 400);}return $product;
}

这个也非常容易理解。我们只需要根据 ID 找到该产品。如果产品不存在,则返回 400 故障响应。否则,将返回产品数组。

接下来是 store 方法

public function store(Request $request)
{$this->validate($request, ['name' => 'required','price' => 'required|integer','quantity' => 'required|integer']);$product = new Product();$product->name = $request->name;$product->price = $request->price;$product->quantity = $request->quantity;if ($this->user->products()->save($product))return response()->json(['success' => true,'product' => $product]);elsereturn response()->json(['success' => false,'message' => 'Sorry, product could not be added'], 500);
}

store 方法中,验证请求中是否包含名称,价格和数量。然后,使用请求中的数据去创建一个新的产品模型。如果,产品成功的写入数据库,会返回成功响应,否则返回自定义的 500 失败响应。

实现 update 方法

public function update(Request $request, $id)
{$product = $this->user->products()->find($id);if (!$product) {return response()->json(['success' => false,'message' => 'Sorry, product with id ' . $id . ' cannot be found'], 400);}$updated = $product->fill($request->all())->save();if ($updated) {return response()->json(['success' => true]);} else {return response()->json(['success' => false,'message' => 'Sorry, product could not be updated'], 500);}
}

update 方法中,我们通过 id 取得产品。如果产品不存在,返回一个 400 响应。然后,我们把请求中的数据使用 fill 方法填充到产品详情。更新产品模型并保存到数据库,如果记录成功更新,返回一个 200 成功响应,否则返回 500 内部服务器错误响应给客户端。

现在,让我们实现 destroy 方法。

public function destroy($id)
{$product = $this->user->products()->find($id);if (!$product) {return response()->json(['success' => false,'message' => 'Sorry, product with id ' . $id . ' cannot be found'], 400);}if ($product->delete()) {return response()->json(['success' => true]);} else {return response()->json(['success' => false,'message' => 'Product could not be deleted'], 500);}
}

destroy 方法中,我们根据 ID 获取产品,如果产品不存在,则返回 400 响应。然后我们删除产品后并根据删除操作的成功状态返回适当的响应。

测试

我们首先来测试身份认证。我们将使用 serve 命令在开发机上启动 Web 服务,你也可以使用虚拟主机代替。运行以下命令启动 Web 服务。

php artisan serve

它将监听 localhost:8000

为了测试 restful API’s,我们使用 Postman。填写好请求体之后,我们请求一下 register 路由。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UkprFcy9-1654620729481)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102194753505.png)]

图表 1 注册测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0yQsS3ST-1654620729482)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102203938920.png)]

图表 2 激活邮件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vJj3CrLy-1654620729483)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204003602.png)]

图表 3 激活成功

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i3Rj8bUj-1654620729484)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204024700.png)]

图表 4 未注册用户登录

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W467w69o-1654620729485)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204034532.png)]

图表 5 未验证用户登录

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z6AHEGp9-1654620729486)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204050371.png)]

图表 6 验证后登录

重置密码模块

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xovHVR4Q-1654620729487)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204111537.png)]

图表 7 不存在用户重置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kHNeFzNN-1654620729488)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204124686.png)]

图表 8 正常用户重置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i0apMcy8-1654620729489)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204136025.png)]

图表 9 重置密码邮件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RefypQ0v-1654620729490)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204149356.png)]

图表 10 未设计前端页面

复制url使用postman测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7ZYJWzIT-1654620729497)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204201689.png)]

图表 11 重置密码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wkP7qmsi-1654620729498)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204215324.png)]

图表 12 重置后旧密码失效

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eQZaenLa-1654620729499)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204228269.png)]

图表 13 新密码成功登录

Products的添加

Store post请求

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RTs9e4K2-1654620729500)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204242830.png)]

图表 14 添加产品

Products的查看用户创建的

Index get请求

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ESctSXgc-1654620729501)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204257318.png)]

图表 15 查看对应用户的产品

查看具体某个id的产品

Show get

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NvzDQgXO-1654620729501)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204307316.png)]

图表 16 查看指定产品信息

更新某个id的属性

Update 使用put

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xDdGUuz0-1654620729502)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204323224.png)]

图表 17 更新产品信息

删除某个产品

Destroy delete请求

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lX3TOdiu-1654620729502)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204338826.png)]

图表 18删除产品

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PqAcscOB-1654620729503)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204353118.png)]

图表 19 删除异常

User

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kbtzUj2h-1654620729504)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204407282.png)]

图表 20 查找用户

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ofn09f6f-1654620729504)(https://gitee.com/maidang-zc/image/raw/master/img/image-20220102204419699.png)]

图表 21 用户登出

参考链接

php加解密函数

https://blog.csdn.net/u013251184/article/details/84493367

Jwt配置参考链接:

链图片转存中…(img-RefypQ0v-1654620729490)]

图表 10 未设计前端页面

复制url使用postman测试

[外链图片转存中…(img-7ZYJWzIT-1654620729497)]

图表 11 重置密码

[外链图片转存中…(img-wkP7qmsi-1654620729498)]

图表 12 重置后旧密码失效

[外链图片转存中…(img-eQZaenLa-1654620729499)]

图表 13 新密码成功登录

Products的添加

Store post请求

[外链图片转存中…(img-RTs9e4K2-1654620729500)]

图表 14 添加产品

Products的查看用户创建的

Index get请求

[外链图片转存中…(img-ESctSXgc-1654620729501)]

图表 15 查看对应用户的产品

查看具体某个id的产品

Show get

[外链图片转存中…(img-NvzDQgXO-1654620729501)]

图表 16 查看指定产品信息

更新某个id的属性

Update 使用put

[外链图片转存中…(img-xDdGUuz0-1654620729502)]

图表 17 更新产品信息

删除某个产品

Destroy delete请求

[外链图片转存中…(img-lX3TOdiu-1654620729502)]

图表 18删除产品

[外链图片转存中…(img-PqAcscOB-1654620729503)]

图表 19 删除异常

User

[外链图片转存中…(img-kbtzUj2h-1654620729504)]

图表 20 查找用户

[外链图片转存中…(img-ofn09f6f-1654620729504)]

图表 21 用户登出

参考链接

php加解密函数

https://blog.csdn.net/u013251184/article/details/84493367

Jwt配置参考链接:

https://segmentfault.com/a/1190000020433890

laravel8+jwt+邮箱注册验证相关推荐

  1. java实现邮箱注册验证

    java实现邮箱验证的功能 在日常生活中,我们在一个网站中注册一个账户时,往往在提交个人信息后,网站还要我们通过手机或邮件来验证,邮件的话大概会是下面这个样子的: 用户通过点击链接从而完成注册,然后才 ...

  2. 电子邮箱注册激活(java)

    大家在注册论坛或者注册一些软件的账号的时候有时需要填写邮箱来注册,而为了测试你的邮箱是否是你自己的会给你的邮箱发送一个激活码链接,只有你点击这个链接之后邮箱才能注册成功,接下来我们就来实现一下这个流程 ...

  3. phpcmsV9 邮箱注册:邮箱验证(不改代码、含演示截图) - 配置篇

    phpcmsV9 邮箱注册:邮箱验证(不改代码.含演示截图) - 全程指导 方法一.(网传) · 配置教程 第一步:修改登陆的验证JS 第二步: 修改登录文件 方法二.真机实操 · 教程 [推荐] 1 ...

  4. mysql查询使用qq邮箱注册_Spring Boot实现qq邮箱验证码注册和登录验证功能

    1.登录注册思路 这是一个使用spring boot做的一个qq邮箱注册和登录的项目. 没写前端页面,使用postman测试.有截图详细. 1.1.思路 注册:通过输入的邮箱发送验证码,检验前端传来的 ...

  5. 发送邮箱验证码进行注册验证

    一.流程图 操作思路 进行邮箱验证码验证 比较与输入的验证码 是否一致 一致的话则可以注册 ** 打开邮箱IMAP/SMTP服务 ** 二.代码示例 (1)生成随机验证码 def get_random ...

  6. 注册验证之邮箱验证(SpringBoot框架)

    一.首先先加入邮箱依赖,看下面链接! JAVA实现邮件发送(SPRING BOOT 框架) 二.大概思路及实现 1.数据库加一个验证字段 2.先让用户进行注册 前端实现:(正常的注册,只是邮箱必填,会 ...

  7. 使用 Java Mail 实现注册时邮箱激活验证功能

    最近在做毕设中实现注册功能时使用到了邮箱激活验证,在此总结下. 简介 JavaMail 是提供给开发者处理电子邮件相关的编程接口,它是 SUN 发布的用来处理 email 的 API ,它可以方便地执 ...

  8. Java 使用邮箱进行注册验证

    1.Springboot 使用邮箱进行注册验证需要导入依赖,如果是Java项目则需要导入email.jar <!--email--> <dependency><group ...

  9. delphi接口带上请求头是什么意思_Gin框架中使用JWT进行接口验证

    背景: 在如今前后端分离开发的大环境中,我们需要解决一些登陆,后期身份认证以及鉴权相关的事情,通常的方案就是采用请求头携带token的方式进行实现.本篇文章主要分享下在Golang语言下使用jwt-g ...

  10. auth0的java-jwt_Spring boot + JWT 实现安全验证 ---auth0.jwt

    auth0的jwt 实现安全验证: 使用自定义参数 和时间戳生成token.验证token时验证自定义参数.auth0.jwt 验证token时会自动验证时间戳是否过期,如果过期,会抛出异常Token ...

最新文章

  1. FATCAT桌面计算器即将出炉!
  2. 图像识别最新赛事!总奖金31万,一起组队吗?
  3. linux变量中代,linux中shell变量$#,$@,$0,$1,$2和变量${0%/*}
  4. jQuery实战之 attr() 和 prop() 的区别
  5. .NET Core 3.0 中的数据库驱动框架 System.Data
  6. 如何使用JPA Type Converter加密数据
  7. fileinputstream读取文件_压缩 20M 文件从 30 秒到 1 秒的优化过程
  8. php界面框架luy_LazyPHP
  9. 如何使用组策略提升Windows系统性能
  10. filebeat配置文件
  11. atat 编译安装测试说明
  12. [工程经验] 电气与控制系统设计方案(框架)- 机器人
  13. Python3 文档批量助手
  14. GMSK的调制与解调
  15. 如何0代码快速搭建教育平台,实现线上直播教学【内附源码/Demo】
  16. 步道乐跑 位置模拟刷步数
  17. 奇点云数据中台技术汇(三)| DataSimba系列之计算引擎篇
  18. 计算某一点绕另一点旋转一定角度后的坐标
  19. Android 组件化代码中心化问题之.api化方案
  20. 北洋UAM-05LX(网口系列适用)ROS节点

热门文章

  1. 强化学习DQN 入门小游戏 最简单的Pytorch代码
  2. php-fpm前台,php-fpm配置文件详解
  3. websphere使用中,比较tomcat
  4. ATECLOUD智能云测试平台,中国人自己的“LABVIEW”-测试测量软件
  5. win快速打开计算机,这十个“Windows+”使用技巧,打开你的电脑技巧新世界
  6. 【mmDetection框架解读】入门篇一、各组件解读,配置文件解读
  7. 医院招聘计算机人员面试经典问题,医院收费员常见面试题目
  8. 如何用计算机弹黑人抬棺简谱,光遇黑人抬棺竖琴简谱 数字简谱简单弹奏教学...
  9. verilogVGA显示太极图案
  10. OpenCV-Python教程