Springboot は JWT をどのように統合して ID 認証を実現しますか?
1. JWT とは
JSON Web トークン (JWT) は、現在最も人気のあるクロスドメイン認証ソリューションです。現在のプロジェクト開発では一般的にフロントエンドとエンドエンドの分離が行われており、これにはクロスドメインと認証の問題が伴います。
2. JWT の構成
ヘッダー、ペイロード、署名の 3 つの部分で構成されます
Header :
ヘッダー情報1. トークンのタイプ、つまり JWT; 2. 使用される署名アルゴリズム (HMASSHA256 や RSA など);
{ "alg": "HS256", "typ": "JWT" }
この json では、typ 属性は、トークン全体がトークンであることを識別するために使用されます。トークン文字列は JWT 文字列です。その alg 属性は、この JWT を発行するときに使用される署名およびダイジェスト アルゴリズムを記述するために使用されます。typ 属性と alg 属性の完全な名前は、実際にはそれぞれ type アルゴリズムです。タイプとアルゴリズムの意味。 3文字で表す理由も、JWTの最終的な文字列サイズを考慮したもので、JWTの名前とも整合性があるため、すべて3文字で …typとalgは、で指定する属性です。 JWT 標準の名前。
ペイロード:
ペイロードは、送信されるデータを運ぶために使用されます。その json 構造は、実際には、JWT によって送信されるデータの宣言のセットです。 JWT 標準ではステートメントはクレームと呼ばれます。その「属性値ペア」の 1 つが実際にはクレーム (要件) であり、各クレームは特定の意味と機能を表します。
クレームにビジネス情報を含めることができます。
署名:
署名は、ヘッダーとペイロードに対応する JSON 構造を Base64URL でエンコードし、「英語のピリオド」で結合した後に得られる 2 つの文字列です。その後、ヘッダーの alg で指定された署名アルゴリズムに従って生成されます。
アルゴリズムが異なれば、署名結果も異なります。 alg: HS256 を例として、前の署名を取得する方法を説明します。
alg の利用可能な値に関する前述の説明によると、HS256 には実際には HMAC アルゴリズムと SHA256 アルゴリズムの 2 つのアルゴリズムが含まれています。前者はダイジェストの生成に使用され、後者はダイジェストのデジタル署名に使用されます。ダイジェスト。これら 2 つのアルゴリズムは、HMACSHA256
jwt データ構造図:
3. JWT の動作原理
1 と総称されることもあります。ログイン リクエストが送信されると、必然的にユーザー情報 uname と pwd
2 が送信されます。ユーザー情報 uname と pwd が正常にログインすると、ユーザー情報は jwt ツールを通じて暗号化された文字列に生成されます。 class
3. 暗号化された文字列は、応答ヘッダーの形式でフロントエンドに送信されます
#4. フロントエンド サーバーには、伝送される jwt 文字列をインターセプトするための応答インターセプターがあります。応答ヘッダーによって、それを Vuex5 に追加します。2 回目のリクエスト時には、フロントエンド サーバーにリクエスト インターセプターがあり、Vuex の jwt 文字列をリクエスト ヘッダー request # に追加します。##6. リクエストがクロスドメイン メソッドを通過してバックエンド サーバーに到達すると、バックエンド サーバーに別のフィルターがあり、リクエスト ヘッダーの jwt 文字列をインターセプトします。 jwt ツール クラスは、jwt 文字列を解析し、それをユーザー情報に解析し、最終的に検証します。バックグラウンド ログイン インターフェイスにアクセスしてログインします。最初にユーザー名とパスワードに基づいてユーザー テーブルにユーザーが存在するかどうかが判断されます。存在する場合、このユーザーに対して jwt 文字列が生成されます。jwt にビジネス情報を追加できます。文字列 (ログイン アカウント、ユーザーの本名など) を取得し、jwt 文字列をフロントエンドに返します。
現在のエンドは jwt 文字列を取得し、それをすべてのリクエストのヘッダーに挿入します。 as token=jwt string
すべてのリクエスト (ログインリクエストはまだ jwt を生成していないため、ログインリクエストを除く) をインターセプトするフィルターをバックエンドで開発し、リクエストのヘッダーから jwt を取得します (つまり、 、トークンの値)、jwt を確認して jwt 内のビジネス情報を取得し、バックエンド インターフェイスがヘッダーから直接ビジネス情報を取得できるように、リクエストのヘッダーにビジネス情報を置きます
フィルターの場合 jwt の有効期限が切れるか検証が失敗した場合、プロンプトがフロントエンドに返され、フロントエンドはログイン ページに戻ってユーザーが再度ログインできるようにします。
1. pom.xml に依存関係を導入します
<!--jwt--> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.8.3</version> </dependency>
2. jwt 生成ツール クラスを開発します。コードは次のとおりです:
package com.lsl.exam.utils; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.lsl.exam.entity.TabUser; import java.util.Date; import java.util.HashMap; import java.util.Map; public class JwtUtil { private static final long EXPIRE_TIME = 1000 * 60 * 60 *24; //设置私钥 private static final String TOKEN_SECRET = "aa082c-66rt89-29sr3t-y9t7b8"; /** * 创建携带自定义信息和声明的自定义私钥的jwt * @param user 用户信息表 * @return jwt串 */ public static String creatJwt(TabUser user){ //构建头部信息 Map<String,Object> header = new HashMap<>(); header.put("typ","JWT"); header.put("alg","HS256"); //根据私钥构建密钥信息 Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); //根据当前用户密码构建密钥信息 // Algorithm algorithm = Algorithm.HMAC256(user.getUserpwd()); //设置过期时间为当前时间一天后 Date nowDate = new Date(); Date expireDate = new Date(System.currentTimeMillis() + EXPIRE_TIME); String jwt = JWT.create().withHeader(header) .withClaim("account",user.getAccount())//业务信息:员工号 .withClaim("username",user.getUsername())//业务信息:员工姓名 .withClaim("rolename",user.getRoleName())//业务信息:角色 .withIssuer("SERVICE")//声明,签名是有谁生成 例如 服务器 .withNotBefore(new Date())//声明,定义在什么时间之前,该jwt都是不可用的 .withExpiresAt(expireDate)//声明, 签名过期的时间 .sign(algorithm);//根据algorithm生成签名 return jwt; } }
3. バックエンド ログイン インターフェイスロジックは次のとおりです:
package com.lsl.exam.controller; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.lsl.exam.entity.TabUser; import com.lsl.exam.entity.backresult.ResultVO; import com.lsl.exam.service.ITabRoleService; import com.lsl.exam.service.IUserService; import com.lsl.exam.utils.Base64Util; import com.lsl.exam.utils.JwtUtil; import com.lsl.exam.utils.ResultVoUtil; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @RequestMapping("/exam") public class UserController { private static final Logger LOG = org.slf4j.LoggerFactory.getLogger("UserController"); @Autowired IUserService userService; @Autowired ITabRoleService roleService; @PostMapping(value = "login",produces = "application/json;charset=UTF-8") @ResponseBody public ResultVO<?> login(@RequestBody Map params){ Map reuslt = new HashMap(); String account = params.get("account") == null ? "" : params.get("account").toString(); String pwd = params.get("pwd") == null ? "" : params.get("pwd").toString(); if ("".equals(account) || "".equals(pwd)){ return ResultVoUtil.error(30000,"用户名或者密码不能为空!"); } //pwd解密 String decodePwd = Base64Util.decode(pwd); if ("".contains(decodePwd)){ return ResultVoUtil.error(30000,"密码错误!"); } TabUser user = userService.getOne(new QueryWrapper<TabUser>() .eq("account",account) .eq("userpwd",decodePwd)); if (null == user){ return ResultVoUtil.error(30000,"用户名或者密码错误"); } //获取当前用户拥有的角色 String userId = user.getId(); Map roleMap = new HashMap(); roleMap.put("userId",userId); List<Map> roleList = roleService.qryRoleInfoByUserId(roleMap); List<String> roleNames = new ArrayList<>(); for(Map role : roleList){ roleNames.add(role.get("role").toString()); } user.setRoleName(JSON.toJSONString(roleNames)); //生成带有业务信息的jwt串 String jwt = JwtUtil.creatJwt(user); //把jwt和当前用户信息返给前端 reuslt.put("jwt",jwt); reuslt.put("roleNames",roleNames); reuslt.put("username",user.getUsername()); reuslt.put("account",user.getAccount()); return ResultVoUtil.success(reuslt); } @PostMapping(value = "qryUser",produces = "application/json;charset=UTF-8") @ResponseBody public Object qryUser(HttpServletRequest request){ //这里header中的信息是filter中放进去的 String account = request.getHeader("account"); String username = request.getHeader("username"); String rolename = request.getHeader("rolename"); List<TabUser> list = userService.list(); return ResultVoUtil.success(list); } }
4. フィルターを開発し、JWT 検証を実行します
package com.lsl.exam.filter; import com.alibaba.fastjson.JSON; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.DecodedJWT; import com.lsl.exam.entity.backresult.ResultVO; import com.lsl.exam.utils.ResultVoUtil; import org.apache.tomcat.util.http.MimeHeaders; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * jwt校验过滤器 */ @Component @WebFilter(filterName = "jwtFilter",urlPatterns = {"/*"}) public class AuthJwtFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; String url = httpServletRequest.getRequestURL().toString(); //配置不进行jwt校验的请求路径 List<String> urlList = new ArrayList<>(); urlList.add("/exam/login"); boolean flag = false; for (String strUrl : urlList){ if (url.contains(strUrl)){ flag = true; } } try { if (!flag){ String token = httpServletRequest.getHeader("token"); //校验token,jwt过期有jwt自行校验,如果超时了,会执行catch里代码 DecodedJWT decodeJwt = JWT.require(Algorithm.HMAC256("aa082c-66rt89-29sr3t-y9t7b8")).build().verify(token); //获取jwt中的业务信息 String account = decodeJwt.getClaim("account").asString(); String username = decodeJwt.getClaim("username").asString(); String rolename = decodeJwt.getClaim("rolename").asString(); Map<String, String> headerMap = new HashMap<>(); headerMap.put("account",account); headerMap.put("username",username); headerMap.put("rolename",rolename); //把业务信息添加到request的header addHeader(httpServletRequest,headerMap); // Class<?> superclass = servletRequest.getClass().getSuperclass().getSuperclass(); // Field requestField = superclass.getDeclaredField("request"); // requestField.setAccessible(true); // RequestFacade requestFacadeInstance = (RequestFacade) requestField.get(servletRequest); RequestFacade requestFacadeInstance = (RequestFacade)superclass3; // Field requestField1 = requestFacadeInstance.getClass().getDeclaredField("request"); // requestField1.setAccessible(true); // Object requestInstance = requestField1.get(requestFacadeInstance); // Field coyoteRequestField = requestInstance.getClass().getDeclaredField("coyoteRequest"); // coyoteRequestField.setAccessible(true); // // Object coyoRequestInstance = requestField1.get(requestInstance); // Field headersField = coyoRequestInstance.getClass().getDeclaredField("headers"); // headersField.setAccessible(true); // // MimeHeaders headers = (MimeHeaders) headersField.get(coyoRequestInstance); // headers.removeHeader("token"); // headers.addValue("account").setString(account); // headers.addValue("username").setString(username); // headers.addValue("roleid").setString(roleid); // } } catch (Exception e) { //jwt校验失败,返给前端的code=1,前端要重定向到登录页面 PrintWriter writer = null; servletResponse.setCharacterEncoding("UTF-8"); servletResponse.setContentType("text/html; charset=utf-8"); try { writer = servletResponse.getWriter(); ResultVO vo = ResultVoUtil.successLogout(); String msg = JSON.toJSONString(vo); writer.println(msg); } catch (IOException ex) { } finally { if (writer != null){ writer.close(); } return; } } filterChain.doFilter(servletRequest,servletResponse); } /** * 向request的header中放业务信息 * @param request * @param headerMap */ private void addHeader(HttpServletRequest request, Map<String, String> headerMap) { if (headerMap==null||headerMap.isEmpty()){ return; } Class<? extends HttpServletRequest> c=request.getClass(); //System.out.println(c.getName()); System.out.println("request实现类="+c.getName()); try{ Field requestField=c.getDeclaredField("request"); requestField.setAccessible(true); Object o=requestField.get(request); Field coyoteRequest=o.getClass().getDeclaredField("coyoteRequest"); coyoteRequest.setAccessible(true); Object o2=coyoteRequest.get(o); System.out.println("coyoteRequest实现类="+o2.getClass().getName()); Field headers=o2.getClass().getDeclaredField("headers"); headers.setAccessible(true); MimeHeaders mimeHeaders=(MimeHeaders) headers.get(o2); for (Map.Entry<String,String> entry:headerMap.entrySet()){ mimeHeaders.removeHeader(entry.getKey()); mimeHeaders.addValue(entry.getKey()).setString(entry.getValue()); } }catch (Exception e){ e.printStackTrace(); } } @Override public void destroy() { } }
以上がSpringboot は JWT をどのように統合して ID 認証を実現しますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









Jasypt の概要 Jasypt は、開発者が最小限の労力で基本的な暗号化機能を自分のプロジェクトに追加できる Java ライブラリであり、暗号化の仕組みを深く理解する必要はありません。一方向および双方向暗号化の高いセキュリティ。標準ベースの暗号化テクノロジー。パスワード、テキスト、数値、バイナリを暗号化します... Spring ベースのアプリケーション、オープン API への統合、JCE プロバイダーでの使用に適しています... 次の依存関係を追加します: com.github.ulisesbocchiojasypt-spring-boot-starter2. 1.1 Jasypt の特典はシステムのセキュリティを保護し、コードが漏洩した場合でもデータ ソースは保証されます。

1. Redis は分散ロックの原則を実装しており、分散ロックが必要な理由 分散ロックについて話す前に、分散ロックが必要な理由を説明する必要があります。分散ロックの反対はスタンドアロン ロックです。マルチスレッド プログラムを作成するとき、共有変数を同時に操作することによって引き起こされるデータの問題を回避します。通常、ロックを使用して共有変数を相互に除外し、データの正確性を確保します。共有変数の使用範囲は同じプロセス内です。共有リソースを同時に操作する必要があるプロセスが複数ある場合、どうすれば相互排他的になるのでしょうか?今日のビジネス アプリケーションは通常マイクロサービス アーキテクチャであり、これは 1 つのアプリケーションが複数のプロセスをデプロイすることも意味します。複数のプロセスが MySQL の同じレコード行を変更する必要がある場合、順序の乱れた操作によって引き起こされるダーティ データを避けるために、分散が必要です。今回導入するスタイルはロックされています。ポイントを獲得したい

Springboot はファイルを読み取りますが、jar パッケージにパッケージ化した後、最新の開発にアクセスできません。jar パッケージにパッケージ化した後、Springboot がファイルを読み取れない状況があります。その理由は、パッケージ化後、ファイルの仮想パスが変更されるためです。は無効であり、ストリーム経由でのみアクセスできます。読み取ります。ファイルはリソースの下にあります publicvoidtest(){Listnames=newArrayList();InputStreamReaderread=null;try{ClassPathResourceresource=newClassPathResource("name.txt");Input

インターネットの発展に伴い、データ対話のための API インターフェイスを提供する必要がある Web サイトやアプリケーションがますます増えています。この場合、APIの認証と暗号化が非常に重要な問題となります。一般的な認証および暗号化メカニズムとして、JWT と JWE が PHP で使用されることが増えています。さて、この記事では、PHP で API 認証と暗号化に JWT と JWE を使用する方法を説明します。 JWT の基本概念 JWT は JSONWe の略です

JWT (JSONWebToken) は、JSON オブジェクトをセキュリティ トークンとして使用して、複数のシステム間でユーザー ID 情報を安全に送信する軽量の認証および認可メカニズムです。 ThinkPHP6 は、PHP 言語をベースとした効率的かつ柔軟な MVC フレームワークであり、JWT 認証メカニズムを含む多くの便利なツールと機能を提供します。この記事では、Web アプリケーションのセキュリティと信頼性を確保するための JWT 認証に ThinkPHP6 を使用する方法を紹介します。

Springboot+Mybatis-plus が SQL ステートメントを使用して複数テーブルの追加操作を実行しない場合、私が遭遇した問題は、テスト環境で思考をシミュレートすることによって分解されます: パラメーターを含む BrandDTO オブジェクトを作成し、パラメーターをバックグラウンドに渡すことをシミュレートします。 Mybatis-plus で複数テーブルの操作を実行するのは非常に難しいことを理解してください。Mybatis-plus-join などのツールを使用しない場合は、対応する Mapper.xml ファイルを設定し、臭くて長い ResultMap を設定するだけです。対応する SQL ステートメントを記述します。この方法は面倒に見えますが、柔軟性が高く、次のことが可能です。

SpringBoot と SpringMVC はどちらも Java 開発で一般的に使用されるフレームワークですが、それらの間には明らかな違いがいくつかあります。この記事では、これら 2 つのフレームワークの機能と使用法を調べ、その違いを比較します。まず、SpringBoot について学びましょう。 SpringBoot は、Spring フレームワークに基づいたアプリケーションの作成と展開を簡素化するために、Pivotal チームによって開発されました。スタンドアロンの実行可能ファイルを構築するための高速かつ軽量な方法を提供します。

1. RedisAPI のデフォルトのシリアル化メカニズムである RedisTemplate1.1 をカスタマイズします。API ベースの Redis キャッシュ実装では、データ キャッシュ操作に RedisTemplate テンプレートを使用します。ここで、RedisTemplate クラスを開いて、クラスのソース コード情報を表示します。publicclassRedisTemplateextendsRedisAccessorimplementsRedisOperations、BeanClassLoaderAware{//キーを宣言、値の各種シリアル化メソッド、初期値は空 @NullableprivateRedisSe
