في الآونة الأخيرة ، واجهت مشكلة في المشروع: يتم فصل الواجهة الأمامية والخلفية ، ويتم تنفيذ الواجهة الأمامية مع VUE ، وتستخدم جميع طلبات البيانات موارد VUE ، ولا يتم استخدام أي شكل ، لذلك يتم استخدام تفاعل البيانات من قبل JSON ، وتستخدم الخلفية التمهيد الربيعي ، ويستخدم التحقق من الإذن أمان الربيع. نظرًا لاستخدام أمان الربيع من قبل ، قاموا بمعالجة الصفحات ، وهذه المرة قاموا ببساطة بمعالجة طلبات AJAX ، لذلك سجلوا بعض المشكلات التي واجهوها. الحل هنا ليس مناسبًا فقط لطلبات AJAX ، ولكن أيضًا يحل التحقق من طلب الهاتف المحمول.
إنشاء مشروع
بادئ ذي بدء ، نحتاج إلى إنشاء مشروع التمهيد الربيع. عند إنشائها ، نحتاج إلى تقديم الويب وأمن الربيع و MySQL و MyBatis (إطار قاعدة البيانات تعسفي بالفعل ، أستخدم myBatis هنا). بعد الإنشاء ، يكون ملف التبعية كما يلي:
<Rependency> <roupeD> org.mybatis.spring.boot </rougiD> <StifactId> mybatis-spring-boot-starter </stifactid> <splection> 1.3.1 </version> </respency> <reperency> <StifactId> Spring-Boot-Starter-Security </stifactid> </sependency> <reperency> <roupiD> org.springframework.boot </rougeid> <StifactId> spring-boot-starter-web </stifactid> <StifactId> mysql-connector-java </stifactid> <scope> وقت التشغيل </scope> </reperence> <reperency> <roupiD> commons-codec </rougeid> <srintifactid> commons-codec </stifactid> <splem
لاحظ أنه تمت إضافة التبعية الأخيرة من Commons-Codec يدويًا. هذا مشروع مفتوح المصدر من Apache يمكن استخدامه لإنشاء هضم رسالة MD5. سأقوم ببساطة بمعالجة كلمة المرور في النص التالي.
قم بإنشاء قاعدة بيانات وتكوينها
من أجل تبسيط المنطق ، قمت بإنشاء ثلاثة جداول هنا ، وهي جدول المستخدم وجدول الدور وجدول رابطة دور المستخدم ، على النحو التالي:
بعد ذلك ، نحتاج إلى تكوين بسيط لقاعدة البيانات الخاصة بنا في Application.properties. نحن هنا محدد من خلال وضعك المحدد.
spring.datasource.url = jdbc: mysql: //vueblogspring.datasource.username=rootspring.datasource.password=123
بناء فئة الكيان
يشير هنا بشكل أساسي إلى بناء فئات المستخدمين. فئات المستخدمين هنا مميزة للغاية ويجب تنفيذ واجهة userDetails ، على النحو التالي:
مستخدم الفئة العامة ينفذ userDetails {private long id ؛ اسم المستخدم الخاص بالسلسلة الخاصة ؛ كلمة مرور السلسلة الخاصة ؛ لقب السلسلة الخاصة تمكين منطقية خاصة ؛ قائمة خاصة <brom> أدوار ؛ Override public boolean isAccountNonexPired () {return true ؛ } Override public boolean isAccountNonlocked () {return true ؛ } Override public boolean iscredentialsnonexpired () {return true ؛ } Override public boolean isEnabled () {return actabled ؛ } Override Public List <GantEdAuthority> getAuthorities () {list <ustralAuthority> electories = new ArrayList <> () ؛ لـ (دور الدور: الأدوار) {exectities.Add (جديد SimplegrantEdauthority ("rol_" + roly.getName ())) ؛ } إرجاع السلطات ؛ } // getter/setter show ...} بعد تطبيق واجهة userDetails ، هناك عدة طرق في هذه الواجهة التي نحتاج إلى تنفيذها. الطرق الأربعة التي تعود منطقية معروفة ومعروفة. يشير التمكين إلى ما إذا كان يتم تمكين حساب الفترة. هذا الحقل موجود في قاعدة البيانات الخاصة بي. لذلك ، وفقًا لنتائج الاستعلام ، يعود الآخر مباشرة لفترات بسيطة. تقوم طريقة getauthorities بإرجاع معلومات الدور للمستخدم الحالي. دور المستخدم هو في الواقع البيانات في الأدوار. يتم تحويل البيانات الموجودة في الأدوار إلى قائمة <ugressauthority> ثم يتم إرجاعها. هناك نقطة يجب ملاحظتها هنا. نظرًا لأن أسماء الأدوار التي أقوم بتخزينها في قاعدة البيانات كلها مثل "المسؤول الفائق" و "المستخدم العادي" ، وما إلى ذلك ، ولا تبدأ بأحرف مثل ROLE_ ، لذلك تحتاج إلى إضافة ROLE_ يدويًا هنا ، تذكر.
هناك أيضًا فئة كيان الدور ، وهي بسيطة نسبيًا ويمكن إنشاؤها وفقًا لحقول قاعدة البيانات. لن أكررها هنا.
إنشاء مستخدمي
usterveservice هنا هي أيضًا خاصة تمامًا ، ومن الضروري تنفيذ واجهة userDetailsService ، على النحو التالي:
servicepublic class userService تنفذ userDetailsService {autowired usermapper usermapper ؛ @autowired rolesmapper rolesmapper ؛ Override public userDetails loadUserByUsername (سلسلة S) يلقي usernamenotfoundException {user user = usermapper.loaduserbyusername (s) ؛ إذا كان (user == null) {// تجنب إرجاع NULL ، فإن هنا إرجاع كائن مستخدم لا يحتوي على أي قيمة ، وسوف يفشل التحقق أيضًا في عملية مقارنة كلمة المرور اللاحقة إرجاع مستخدم جديد () ؛ } // Quase معلومات دور المستخدم والعودة إلى المستخدم. قائمة <row> أدوار = rolesmapper.getRolesByuid (user.getId ()) ؛ user.setRoles (أدوار) ؛ إرجاع المستخدم ؛ }}بعد تنفيذ واجهة userDetailsService ، نحتاج إلى تنفيذ طريقة loaduserbyusername في الواجهة ، أي استعلام المستخدم استنادًا إلى اسم المستخدم. يتم حقن اثنين من التعيينات في MyBatis هنا ، ويستخدم UserMapper للاستعلام عن المستخدمين ، ويتم استخدام RolesMapper للاستعلام عن الأدوار. في طريقة LoadUserByUserName ، قم أولاً بالاستعلام عن المستخدم وفقًا للمعلمات التي تم تمريرها (المعلمة هي اسم المستخدم الذي تم إدخاله عند تسجيل الدخول إلى المستخدم). إذا تم العثور على المستخدم NULL ، فيمكنك رمي استثناء USERNAMENOTFOUNDEXCESTION مباشرة. ومع ذلك ، لراحة المعالجة ، قمت بإرجاع كائن مستخدم دون أي قيمة. وبهذه الطريقة ، أثناء عملية مقارنة كلمة المرور اللاحقة ، ستجد أن تسجيل الدخول فشل (هنا يمكنك ضبطه وفقًا لاحتياجات عملك). إذا لم يتم العثور على المستخدم NULL ، فسنقوم بالاستعلام عن دور المستخدم استنادًا إلى معرف المستخدم ونضع نتيجة الاستعلام في كائن المستخدم. سيتم استخدام نتيجة الاستعلام هذه في طريقة getAuthorities لكائن المستخدم.
تكوين الأمان
لنلقي نظرة على تكوين الأمان الخاص بي أولاً ، ثم سأشرحه واحدًا تلو الآخر:
ConfigurationPublic Class WebSecurityConfig يمتد WebSecurityConfigureRadapter {AUTOWired Userveservice userservice ؛ Override proted void configure (antainticationManagerBuilder) يلقي استثناء {auth.userDetailsService (userService) .PasswordEncoder (new PasswordEncoder () {chariveride public encode () param charquence plaintext* param s ciphertext* regurn*/ Override مباريات منطقية عامة (charquencence charquence ، string s) {return s.equals (digestutils.md5digestashex (charquence.toString ().)) ؛ } override contected void config (httpsecurity http) يلقي استثناء {http.authorizerequests () .antmatchers ("/admin/**"). hasrole ("Super Admin") .anyrequest (). مصادقة () // مسارات أخرى يتم الوصول إليها بعد تسجيل الدخول in.and. httpservletsponse.setConttype ("application/charset = utf-8") ؛ .failureHandler (مصادقة جديدة failureHandler () {Override public void onauthenticationfailure (httpservletrequest httpservletrequest ، httpservletponse httpservletresponse ، antainticationexception e) trows ioexception ، servlexception { httpservletsponse.setConttype ("application/charset = utf-8") ؛ ). } Override public void config (WebSecurity Web) يلقي الاستثناء {web.ignoring (). antmatchers ("/reg") ؛ }}هذا هو جوهر التكوين لدينا. من فضلك استمع إلي:
1. أولاً وقبل كل شيء ، هذه فئة تكوين ، لذلك تذكر إضافة تعليق توضيحي التكوين. لأن هذا هو تكوين أمان الربيع ، يجب أن ترث websecurityConfigureRadapter.
2. حقن مستخدمي المستخدمين الذي تم إنشاؤه للتو وسوف نستخدمه لاحقًا.
3. التكوين (AuthenticationManagerBuilder Auth) تستخدم طريقة لتكوين طريقة المصادقة الخاصة بنا ، وتمرير المستخدمين في Auth.UserDetailsService () بحيث يتم تحديد طريقة LoadUserByUsernam هناك حاجة أيضًا إلى كلمة مرور النص العادي لمعالجتها عند تسجيل الدخول ، لذلك أضفت PasswordEncoder ، وبعد إضافة PasswordEncoder ، يمكنني جديد مباشرة فئة داخلية مجهولة من كلمة المرور. هناك طريقتان للتنفيذ هنا ، ويمكنك معرفة معنى الطريقة من خلال النظر إلى الاسم. من الواضح أن الطريقة الأولى تشفير النص العادي. هنا أستخدم MD5 Message Digest. يتم توفير طريقة التنفيذ المحددة من خلال الاعتماد على العموم-كودك ؛ تتطابق الطريقة الثانية من مقارنة كلمة المرور ، ومعلمتان ، والمعلمة الأولى هي كلمة مرور النص العادي ، والثاني هو النص المشفر. هنا تحتاج فقط إلى تشفير النص العادي ومقارنته بالنص المشفر (إذا كنت مهتمًا بذلك ، فيمكنك الاستمرار في التفكير في إضافة الملح إلى كلمة المرور).
4. التكوين (HTTPSecurity HTTP) يستخدم لتكوين قواعد المصادقة الخاصة بنا /admin/** وما إلى ذلك رأيت على الإنترنت أن أصدقائي لديهم أسئلة حول إضافة ROLE_ في طريقة Hassrole. لا تضيفه هنا. إذا كنت تستخدم طريقة hasauthority ، فأنت بحاجة إلى إضافتها. تعني anyRequest (). المصادقة () أن جميع المسارات الأخرى تحتاج إلى مصادقة/تسجيل الدخول قبل الوصول. بعد ذلك قمنا بتكوين صفحة تسجيل الدخول كـ login_page ، مسار معالجة تسجيل الدخول هو /تسجيل الدخول ، اسم المستخدم تسجيل الدخول هو اسم المستخدم ، وكلمة المرور هي كلمة المرور. قمنا بتكوين هذه المسارات للوصول مباشرةً ، ويمكن أيضًا الوصول إلى تسجيل الخروج من تسجيل الدخول مباشرة ، وأخيراً أغلق CSRF. في SuccessHandler ، استخدم استجابة لإرجاع JSON الذي قام بتسجيل الدخول بنجاح. تذكر عدم استخدام DefaultSuccessurl. DefaultSuccessurl هي صفحة يتم إعادة توجيهها فقط بعد تسجيل الدخول بنجاح. يتم استخدام نفس السبب أيضًا لـ FailureHandler.
5. لقد قمت بتكوين بعض قواعد التصفية في طريقة التكوين (WebSecurity Web) ، لذلك لن أخوض في التفاصيل.
6. بالإضافة إلى ذلك ، بالنسبة للملفات الثابتة ، مثل /images/** ، /css/** ، و /js/** ، لم يتم اعتراض الافتراضي.
وحدة تحكم
أخيرًا ، دعونا نلقي نظرة على وحدة التحكم الخاصة بنا ، على النحو التالي:
RestControllerPublic Class LoginRegController {/** * * إذا قفزت تلقائيًا إلى هذه الصفحة ، فهذا يعني أنه لم يتم تسجيل الدخول إلى المستخدم ، ويمكنك أن تعيد الموجه المقابل * <p> * إذا كنت ترغب في دعم تسجيل الدخول إلى النما logInPage () {return New Respbean ("error" ، "لم يتم تسجيل الدخول حتى الآن ، يرجى تسجيل الدخول!") ؛ }} وعموما ، هذه وحدة التحكم بسيطة نسبيا. Respbean يعيد JSON بسيط ، وهو غير مفصل. ما تحتاج إلى الانتباه إليه هنا هو login_page . صفحة تسجيل الدخول التي قمنا بتكوينها هي login_page . ولكن في الواقع ، فإن login_page ليس صفحة ، ولكن JSON. هذا لأنه عندما أزور صفحات أخرى دون تسجيل الدخول ، فإن Spring Security سيقفز تلقائيًا إلى صفحة login_page . ومع ذلك ، في طلب Ajax ، ليس هناك حاجة إلى هذا النوع من القفزة. كل ما أريده هو موجه لتسجيل الدخول أم لا ، حتى أتمكن من إعادة JSON هنا.
امتحان
أخيرًا ، يمكن للأصدقاء استخدام أدوات مثل Postman أو RestClient لاختبار مشكلات تسجيل الدخول والإذن ، لذلك لن أظهرها.
حسنًا ، بعد المقدمة أعلاه ، أعتقد أن الأصدقاء قد فهموا بالفعل معالجة Spring Boot+Spring Security لطلبات تسجيل الدخول إلى Ajax. حسنًا ، هذا كل شيء لهذا المقال. إذا كان لديك أي أسئلة ، فيرجى ترك رسالة لمناقشة.