مقدمة
عند استخدام SpringCloud لإنشاء نظام موزع مع بنية الخدمات الصغيرة ، يعد OAUTH2.0 معيار الصناعة لإصدار الشهادات. يوفر Spring Security OAUTH2 أيضًا مجموعة كاملة من الحلول لدعم استخدام OAUTH2.0 في بيئة SPRING Cloud/Spring Boot ، مما يوفر مكونات خارج الصندوق. ومع ذلك ، أثناء عملية التطوير ، سنجد أنه نظرًا لأن مكونات SPRING Security Security OAUTH2 شاملة بشكل خاص ، فإن هذا يجعل من غير المريح للغاية أو ليس من السهل تحديد حل التمديد مباشرة ، مثل:
عند مواجهة هذه السيناريوهات ، من المتوقع ألا يتمكن الكثير من الأشخاص الذين ليسوا على دراية بأمن Spring Security Oauth2. استنادًا إلى متطلبات السيناريو المذكورة أعلاه ، كيفية دمج تسجيل دخول رمز التحقق من الرسائل القصيرة بأناقة وتسجيل الدخول إلى الطرف الثالث ، وكيفية اعتبارها متكاملة بأناقة؟ هناك المتطلبات التالية:
استنادًا إلى متطلبات التصميم أعلاه ، سنقدم بالتفصيل كيفية تطوير مجموعة من مكونات مصادقة تسجيل الدخول المتكاملة لتلبية المتطلبات المذكورة أعلاه في المقالة.
اقرأ هذا المقال الذي تحتاج إلى معرفته حول نظام شهادة OAUTH2.0 و Springboot و SpringSecurity و Spring Cloud وغيرها من المعرفة ذات الصلة
الأفكار
دعونا نلقي نظرة على عملية مصادقة أمان الربيع OAUTH2:
في هذه العملية ، لا يوجد العديد من نقاط الدخول ، وفكرة تسجيل الدخول المتكاملة هي كما يلي:
بعد الوصول إلى هذه العملية ، يمكنك بشكل أساسي دمج تسجيل الدخول إلى الطرف الثالث بأناقة.
ينجز
بعد تقديم الأفكار ، يوضح الرمز التالي كيفية تنفيذها:
الخطوة الأولى هي تحديد اعتراض طلبات تسجيل الدخول
/** * Author liqiu * date 2018-3-30 **/ @ @componentpublic class integrationAuthenticationFilter يمتد genericfilterbean الأدوات ApplicationContextAware {private static string ulet_type_parm_name = "auth_type" ؛ Static Final Final String Oauth_token_url = "/oauth/token" ؛ مجموعة خاصة <EnctionAuthentIcator> المصادقون ؛ application applicationcontext applicationContext ؛ requestmatcher requestmatcher الخاص ؛ IntegrationAuthenticationFilter () {this.requestmatcher = new orrequestMatcher (جديد antpathrequestmatcher (oauth_token_url ، "get") ، جديد antpathrequestmatcher (oauth_token_url ، "post")) ؛ } Override public void dofilter (servletrequest servletrequest ، servletResponse servletResponse ، filterchain filterchain) يلقي ioException ، servletexception {httpservletrequest request = (httpservletrequest) servleTrequest ؛ httpservletresponse استجابة = (httpservletresponse) servletResponse ؛ if (requestmatcher.matches (request)) {// تعيين IntegrationAuthentication IntegrationAuthentication integrationAuthentication = IntegrationAuthentication () ؛ integrationAuthentication.setauthtype (request.getParameter (auth_type_parm_name)) ؛ integrationAuthentication.setauthparameters (request.getParamEterMap ()) ؛ integrationAuthenticationContext.set (integrationAuthentication) ؛ حاول {// معالجة هذا. filterchain.dofilter (طلب ، استجابة) ؛ // post-processing this.complete (integrationAuthentication) ؛ } أخيرًا {integrationAuthenticationContext.clear () ؛ }} آخر {filterchain.dofilter (طلب ، استجابة) ؛ }} / *** preprocessing* param integrationAuthentication* / private void إعداد (integrationAuthentication integrationAuthentication) {// lazy loading antensicator if (this.auhenticators == null) {Synchronized ApplicationContext.getBeansofType (integrationAuthenticator.class) ؛ if (integrationAuthentIcAtormap! = null) {this.authenticators = integrationAuthentIcAtormap.values () ؛ }}} if (this.authenticators == null) {this.authenticators = new ArrayList <> () ؛ } لـ (integrationAuthentIcator Authenticator: Authenticators) {if (Australiticator.Support (integrationAuthentication)) {antainicator.prepare (integrationAuthentication) ؛ }}} / *** post-processing* param integrationAuthentication* / private void complete (integrationauthentication integrationAuthentication) {for (integrationAuthentIcator Authentication: antaintications) {if (antainticator.support (integrationauthentication)) }}} Override public void setapplicationContext (ApplicationContext ApplicationContext) يلقي beansexception {this.applicationContext = ApplicationContext ؛ }}في هذه الفئة ، يتم الانتهاء من جزءين من العمل بشكل أساسي: 1. الحصول على نوع المصادقة الحالي وفقًا للمعلمات ، 2. استدعاء integrationauthenticator.
الخطوة 2: ضع التقاطع في سلسلة التقاطع
/** * Author liqiu * date 2018-3-7 **/ @configuration @enableAuthorizationSerVerpublic Class SuapterizationSerVerConfiguration يمتد uventizationSerConfigureRadapter {autowired private redisconnectionfactoryfactory ؛ @AUTOWIRED InitienticationManager AuthenticationManager ؛ AUTOWIRED private integrationUserDetailsService IntegrationUserDetailsService ؛ Autowired Private WebresponseExceptionTranslator WebresponseExceptionTransLator ؛ AUTOWIRED IntegrationAuthenticationFilter IntegrationAuthenticationFilter ؛ AUTOWIRED DATABASECACHABLECLIENTDETAILSSERVICE REDISCLIENTDETAILSService ؛ Override public void تكوين (ClientDetailsServicEconFigurer CONSTRAL) يلقي الاستثناء {// TODO يظل تفاصيل العملاء العملاء. } Override public void config (ualdizationServerEndPointSconfigurer endpoints) {نقاط النهاية. .reuserefreshtokens (false) .userDetailsService (IntegrationUserDetailsService) ؛ } override public void config (suapterizationServersecurityConfigurer Security) يلقي استثناء {security.allowformauthenticationforclients () .TokenKeyAccess ("isauthenticated ()) } bean provorpordencoder passworderencoder () {return new BcryptPasswordEncoder () ؛ } bean public jwtaccesstokenconverter jwtaccesstokenconverter () {jwtaccesstokenconverter jwtaccesstokenconverter = new jwtaccesstokenconverter () ؛ jwtaccesstokenconverter.setsigningkey ("cola-cloud") ؛ إرجاع jwtaccesstokenconverter ؛ }}ضع التقاطع في سلسلة المصادقة عن طريق استدعاء الأمن. .addTokenEndPointAuthenticationFilter (IntegrationAuthenticationFilter) ؛ طريقة.
الخطوة 3: معالجة معلومات المستخدم وفقًا لنوع المصادقة
servicepublic class integrationUserDetailsService تنفذ userDetailsService {autowired upmclient upmclient ؛ قائمة خاصة <NudgeRationAuthentIcator> المصادقون ؛ @autowired (مطلوب = خطأ) public void setInteMrationAuthentIcators (قائمة <NutegrationAuthentIcator> المصادقة) {this.authenticators = antainicators ؛ } Override Public User LoadUserByUsername (اسم المستخدم السلسلة) يلقي usernamenotfoundException {integrationauthentication integrationAuthentication = integrationAuthenticationContext.get () ؛ // judge ما إذا كان تسجيل دخول متكامل إذا (integrationAuthentication == null) {integrationAuthentication = new integrationAuthentication () ؛ } integrationAuthentication.setuserName (اسم المستخدم) ؛ uservo uservo = this.authenticate (integrationAuthentication) ؛ if (uservo == null) {رمي usernamenotfoundException جديد ("اسم المستخدم أو خطأ كلمة المرور") ؛ } المستخدم = مستخدم جديد () ؛ BeanUtils.copyProperties (Uservo ، المستخدم) ؛ this.setAuthorize (المستخدم) ؛ إرجاع المستخدم ؛ } / ** * تعيين معلومات التخلص * * param user * / public void setAuthorize (مستخدم المستخدم) {Authorize = this.upmclient.getauthorize (user.getId ()) ؛ user.setRoles (election.getRoles ()) ؛ user.setResources (Outlize.getResources ()) ؛ } ustervo ustercate (integrationAuthentication integrationAuthentication) {if (this.authenticators! = null) {for (integrationAuthenticator Authenticator: Authenticators) {if (antegulticator.support (integrationauthentication)) }} الإرجاع null ؛ }}هنا هو integrationuserDetailsService. سيتم استدعاء طريقة المصادقة في طريقة LoadUserByUsername. في طريقة المصادقة ، سيقوم نوع مصادقة السياق الحالي بالاتصال بـ IntegrationAuthentIcator المختلفة للحصول على معلومات المستخدم. دعنا نلقي نظرة على كيفية معالجة اسم المستخدم وكلمة المرور الافتراضية:
@component @primarypublic class usernamepasswordauthenticator يمتد ملخص uScerparableInteGrationAuthentIcator {Autowired private ucclient ucclient ؛ Override Public Uservo المصادقة (IntegrationAuthentication integrationAuthentication) {return ucclient.finduserbyusername (integrationAuthentication.getuserName ()) ؛ } Override public void إعداد (integrationAuthentication integrationAuthentication) {} Override Public Boolean Support (IntegrationAuthentication integrationAuthentication) {return stringUtils.isempty (integrationauthentication.getauthtype ()) ؛ }}لن يتعامل usernamepasswordauthenticator فقط مع نوع المصادقة الافتراضي دون نوع المصادقة المحدد. تحصل هذه الفئة بشكل أساسي على كلمات المرور من خلال اسم المستخدم. بعد ذلك ، دعونا نلقي نظرة على كيفية التعامل مع تسجيل الدخول إلى رمز التحقق من الصورة:
/*** مصادقة رمز التحقق المتكامل* Author liqiu* date 2018-3-31 **/ @componentpublic class classeintegrationauthenticator @autowired VCCClient vccclient ؛ Override public void إعداد (IntegrationAuthentication integrationAuthentication) {String vCtoken = integrationAuthentication.getauthParameter ("vc_token") ؛ String vccode = integrationAuthentication.getAuthParameter ("vc_code") ؛ // التحقق من التحقق coderesult <Ololean> النتيجة = vccclient.validate (vctoken ، vccode ، null) ؛ if (! result.getData ()) {رمي Oauth2Exception جديد ("خطأ رمز التحقق") ؛ }} Override Public Boolean Support (integrationAuthentication integrationAuthentication) {return fearification_code_auth_type.equals (integrationauthentication.getauthtype ()) ؛ }}يرث VerificationCodeIntegrationAuthenticator usernamepasswordauthenticator لأنه يحتاج فقط إلى التحقق مما إذا كان رمز التحقق صحيحًا في طريقة التحضير ، وما إذا كان المستخدم قد حصل عليه باستخدام اسم المستخدم وكلمة المرور. ومع ذلك ، فإن نوع المصادقة هو "VC" قبل معالجته. دعونا نلقي نظرة على كيفية معالجة تسجيل دخول رمز التحقق من الرسائل القصيرة:
@componentpublic class smsintegrationauthenticator يمتد تجريبي inclableInteGrationAuthentIcator تنفذ ApplicationEventPublisherAware { @autowired ucclient ucclient ؛ @autowired VCCClient vccclient ؛ Autowired passwordercoder passwordencoder ؛ تطبيق applicationeventpublisher ApplicationeventPublisher ؛ السلسلة الثابتة النهائية الخاصة sms_auth_type = "sms" ؛ Override Public Uservo مصادقة (IntegrationAuthentication integrationAuthentication) {// Get Password ، القيمة الفعلية هي كلمة مرور سلسلة التحقق = integrationauthentication.getauthparameter ("كلمة المرور") ؛ // الحصول على اسم المستخدم ، والقيمة الفعلية هي اسم سلسلة الهاتف المحمول المستخدم = integrationAuthentication.getUserName () ؛ // نشر الأحداث ، يمكنك الاستماع إلى الأحداث لتسجيل المستخدم تلقائيًا this.ApplicationEventPublisher.PublishEvent (SMSAuthenticateBeorevent (IntegrationAuthentication)) ؛ // Query User من خلال رقم الهاتف المحمول Uservo uservo = this.ucclient.finduserbyphonenber (اسم المستخدم) ؛ if (uservo! = null) {// قم بتعيين كلمة المرور كرمز التحقق uservo.setPassword (passwordencoder.encode (password)) ؛ // نشر الأحداث ، يمكنك الاستماع إلى أحداث إخطار الرسائل This.ApplicationEventPublisher.PublishEvent (SMSAuthenticatesuccessevent (IntegrationAuthentication)) ؛ } إرجاع Uservo ؛ } Override public void إعداد (IntegrationAuthentication integrationAuthentication) {String smstoken = integrationauthentication.getauthparameter ("sms_token") ؛ String smscode = integrationAuthentication.getauthparameter ("كلمة المرور") ؛ username string = integrationAuthentication.getauthparameter ("اسم المستخدم") ؛ النتيجة <Ololean> النتيجة = vccclient.validate (smstoken ، smscode ، اسم المستخدم) ؛ if (! result.getData ()) {رمي Oauth2Exception جديد ("خطأ رمز التحقق أو انتهاء صلاحيته") ؛ }} Override Public Boolean Support (IntegrationAuthentication integrationAuthentication) {return sms_auth_type.equals (integrationauthentication.getauthtype ()) ؛ } Override public void setapplicationeventpublisher (applicationeventpublisher applicationeventpublisher) {this.applicationEventPublisher = ApplicationeventPublisher ؛ }}ستعمل SMSIntegrationAuthenticator على معالجة رمز التحقق من الرسائل القصيرة المسجلة لتحديد ما إذا كان غير قانوني. إذا كان غير قانوني ، فسوف يقطع تسجيل الدخول مباشرة. إذا تم تمرير المعالجة المسبقة ، سيتم الحصول على معلومات المستخدم من خلال رقم الهاتف المحمول عند الحصول على معلومات المستخدم ، وسيتم إعادة تعيين كلمة المرور لتمرير التحقق من كلمة المرور اللاحقة.
لخص
في هذا الحل ، فإن الاستخدام الرئيسي لسلسلة المسؤولية ونمط تصميم المحول لحل مشكلة تسجيل الدخول المتكاملة ، ويحسن قابلية التوسع ، ولا يلوث رمز الربيع المصدر. إذا كنت تريد أن ترث تسجيلات تسجيلات أخرى ، فأنت بحاجة فقط إلى تنفيذ IntegrationAuthenticator مخصص.
عنوان المشروع: https://gitee.com/leecho/cola-cloud
التنزيل المحلي: cola-cloud_jb51.rar
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.