この記事では、スプリングブートとシュリオを組み合わせてJWTを実装する方法を紹介し、次のように共有します。
検証に関しては、およそ2つの側面があります。
主なソリューション:カスタムシロフィルターを使用します
プロジェクト構築:
これはスプリングブートWebプロジェクトです。 Spring-Bootプロジェクトの構築について知らない場合は、Googleにしてください。
POM.MXは、関連するJARパッケージを導入します
<! - Shiro Permission Management - > <Dependency> Groupid> org.apache.shiro </groupid> <artifactid> shiro-spring </artifactid> <version> $ {shiro.version} </version <バージョン> $ {shiro.version} </version> </dependency> <! - jwt-> <依存関係> <groupid> io.jsonwebtoken </groupid> <artifactid> jjwt </artifactid> <バージョンシュリオ関連の構成
ポイントを作ってください! !フィルターをカスタマイズしました
filtermap.put( "jwtfilter"、new jwtfilter());
@configurationPublic Class Shiroconfig {@Bean Public ShirofilterFactoryBean GetShirofilterFactoryBean(SecurityManager SecurityManager){ShirofilterFactoryBean ShirofilterFactorybean = new Shirofilterfactorybean(); shirofilterfactorybean.setsecuritymanager(securitymanager); //独自のフィルターを追加して、jwtfilterマップ<文字列、フィルター> filtermap = new Hashmap <>(); filtermap.put( "jwtfilter"、new jwtfilter()); shirofilterfactorybean.setfilters(filtermap); / * *カスタムURLルール * http://shiro.apache.org/web.html#urls- */map <string> filterchaindefinitionmap = shirofilterfactorybean.getFilterchaindefinitionMap(); FilterChainDefinitionMap.put( "/**"、 "jwtfilter"); shirofilterfactorybean.setFilterChainDefinitionMap(FilterChainDefinitionMap); shirofilterfactorybeanを返します。 }/** * SecurityManagerは、ShirodBrealmを直接注入する必要はありません。これにより、ソリューションのトランザクション障害を引き起こす可能性があります。Http://www.debugrun.com/a/nks9ejq.html */@bean( "securitymanager")公開デフォルトウェブシュートママンガーマネージャー( DefaultWebseCurityManager Manager = new DefaultWebseCurityManager(); Manager.setRealm(tokenrealm); / * * shiroに付属のセッションを閉じます。詳細については、ドキュメントを参照してください * http://shiro.apache.org/session-management.html#sessionmanagement-statelessapplications%28sessionless%29 */defaultsubjectdao subjectdao = new defaultsubjectdao(); defaultsessionStorageEvaluator defaultsessionStorageEvaluator = new DefaultSessionStorageEvaluator(); defaultsessionStorageEvaluator.SetsessionStorageEnabled(false); subjectdao.setsessionStorageEvaluator(defaultsessionStorageEvaluator); Manager.setsubjectdao(subjectdao); Return Manager; } @Bean Public LifeCycleBeanPostProcessor lifecyclebeanpostprocessor(){return new lifecyclebeanpostprocessor(); } @bean(name = "tokenrealm")@dependson( "lifecyclebeanpostprocessor")public tokenrealm tokenrealm(){return new tokenrealm(); } @bean @dependson( "lifecyclebeanpostprocessor")public defaultadvisorautoproxycreator defaultadvisorautoproxycreator(){defaultadvisorautoproxycreator defaultadvisorautoproxycreator = new defaultadvisoroutoproxcreator(); // cglibがプロキシと可能なプロキシエラーの重複を防ぐように強制しますdefaultadvisorautoproxycreatorを返します。 } @Bean Public AutherizationAttributesOurCeadVisor getAuthorizationAttributeSourCeadVisor(SecurityManager securityManager){AuthizationAtatistributeSourCeadVisor AutherizationAttributesOurCeadVisor =新しいAuthorizationAttributesCeadVisor(); authisizationAttributesourceadvisor.setsecuritymanager(securitymanager);新しいauthorizationAttributesourceadvisor()を返します。 }}シュリオフィルターをカスタマイズします
実行順序:prehandle-> dofilterinternal-> executelogin-> onloginsuccess
主な判断は、ログイン要求がdofilterinternalであるかどうかです
Public Class JWTFilterは、basichttpauthenticationfilter { / ***ログインを実行する方法をカスタマイズします* / @OverrideプロテクションBoolean executelogin(servletRequest request、servletResponse応答)IOException {httpservletrequest httpservletrequest =(httpservletrequest =(httpservletrequest =(httpservletrequest =(httpservletrequest)) usernamepasswordtoken usernamepasswordtoken = json.parseobject(httpservletrequest.getinputStream()、usernamepasswordtoken.class); //ログインのためにそれをレルムに送信します。エラーが間違っている場合、それは例外をスローし、被験者の被験者= this.getSubject(request、response)をキャッチします。 subject.login(usernamepasswordtoken); this.onloginsuccess(usernamepasswordtoken、件名、リクエスト、応答)を返します。 //例外をスローする}/ ***実行する最初の方法*/ @Override保護されたBoolean Prehandle(servletRequest request、servletResponse応答)スロー{return super.prehandle(request、response); } / ***ログイン操作が成功した後、ログイン* JWTのヘッダーを追加* / @OverrideプロテクションBoolean onloginsuckes(authenticationTokenトークン、件名、サーブレクストリクエスト、サーブレスポンス応答){httpservletResponse httpservletresponse =(httpservletresponse)応答。 String jwttoken = jwts.builder().setid(token.getPrincipal()。toString()).setexpiration(datetime.now()。プラスマイテス(30).todate()).signwith(signaturealgorithm.hs256、jwtcost.sinationkey).compact(); httpservletresponse.addheader(authorization_header、jwttoken); trueを返します。 } / ***ログインと検証の主なプロセス*ログインかどうか、またはログイン後の通常の要求を決定します* / @Override public void dofilterinternal(servletrequest servletrequest、servletresponse servletresponse、filterchain filterchain) (httpservletrequest)servletrequest; httpservletResponse httpservletResponse =(httpservletResponse)servletResponse; string servletpath = httpservletrequest.getServletPath(); if(stringutils.equals(servletpath、 "/login")){// executelogin(servletRequest、servletResponse); } else {string authenticationheader = httpservletrequest.getheader(autherization_header); if(stringutils.isnotempty(authenticationheader)){chails body = jwts.parser().setsiingkey(jwtcost.signaturekey).parseclaimsjws(authenticationheader).getbody(); if(body!= null){// token body.setexpiration(dateTime.now()。プラスマインス(30).todate())を更新します。 string updateToken = jwts.builder()。setclaims(body).compact(); httpservletresponse.addheader(authorization_header、updatetoken); //ユーザー資格情報の追加プリンシパルコレクションプリンシパル= new SimplePrincipalCollection(body.getId()、jwtcost.usernamepasswordrealm); // shiro shiro user information websubject.builder builder = new websubject.builder(servletrequest、servletresponse); Builder.Principals(プリンシパル); builder.authenticated(true); Builder.SessionCreationEnabled(false); websubject subject = builder.buildwebsubject(); //コンテナに入れて、threadcontext.bind(subject)を呼び出します。 filterchain.dofilter(httpservletrequest、httpservletresponse); }} else {httpservletresponse.setstatus(httpstatus.forbidden.value()); }}}}ログイン処理に失敗しました
シュリオの例外を処理します
@RestControllerAdvicePublic Class GlobalControllerExceptionHandler {@exceptionHandler(value = exception.class)public object allexceptionhandler(httpservletrequest request、httpservletResponse応答、例外){string message = exception.getCause()。getMessage(); logutil.error(メッセージ); new resultinfo(exception.getClass()。getName()、message); } /*==================================================================== Response.setStatus(httpstatus.forbidden.value()); 「誤った性的セクシェンス」を返します。 } @exceptionHandler(value = nowedaccountexception.class)public string nowncountexception(httpservletrequest request、httpservletResponse応答、例外例外){response.setStatus(httpstatus.forbidden.value()); "nownccountexception"を返します。 } @exceptionhandler(value = lockedaccountexception.class)public string lockedaccountexception(httpservletrequest request、httpservletResponse応答、例外){response.setStatus(httpstatus.forbidden.value()); "lockedacountexception"を返します。 } @exceptionHandler(value = offeriveattempsexception.class)public string offoriveattemptecection(httpservletrequest request、httpservletResponse応答、例外例外){response.setStatus(httpstatus.forbidden.value()); "overiveatemptsexception"を返します。 } @exceptionHandler(value = authenticationexception.class)public string authenticationexception(httpservletrequest request、httpservletResponse応答、例外){respons.setStatus(httpstatus.forbidden.value()); "AuthenticationException"を返します。 } @exceptionHandler(value = unauthorizedexception.class)public string unauthorizedexception(httpservletrequest request、httpservletResponse応答、例外){response.setStatus(httpstatus.forbidden.value()); 「UnauthorizedException」を返します。 }}JWTの例外を処理します
これは、フィルターで発生する例外であり、 @exceptionHandlerがインターセプトできないため、これは落とし穴です。
/***スプリングブートエラーページをインターセプトします*/ @retscontrollerpublic class globalexceptionhandler explention errorcontroller {@override public string geterrorpath(){return "/error"; } @RequestMapping(value = "/error")public Objectエラー(httpservletrequest request、httpservletResponse応答)スロー例外{//ロジック例外=(例外)request.getAttribute( "javax.servlet.error.exception");スロー可能な原因= Exception.getCause(); if(instanceof expiredjwtexception){respons.setstatus(httpstatus.gateway_timeout.value()); new resultinfoを返します( "expiredjwtexception"、cound.getmessage()); } if(malformedjwtexceptionのinstance){respons.setstatus(httpstatus.forbidden.value()); new resultinfoを返します( "malformedjwtexception"、cound.getMessage()); } new resultInfo(cause.getCause()。getMessage()、cound.getMessage())を返します。 }}許可などの承認情報に関して、Redisに直接キャッシュすることができます。それもいいと思います。
ソースコードプレゼント:Githup-Shiro Branch:Warm Reminder:テストコードは日常生活で乱雑になる可能性があります。
上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。