JavaRush /مدونة جافا /Random-AR /دعونا نقدم تسجيل الدخول المنتظم عبر البريد الإلكتروني وOA...

دعونا نقدم تسجيل الدخول المنتظم عبر البريد الإلكتروني وOAuth2 إلى Spring Security باستخدام مثال خدمة الملاحظات

نشرت في المجموعة
أثناء كتابة طلبي، واجهت نقصًا في المقالات الواضحة حول كيفية جعل المستخدم يقوم بالتسجيل عبر البريد الإلكتروني وشبكات التواصل الاجتماعي. كانت هناك دروس تعليمية جيدة حول إعداد نموذج تسجيل الدخول الكلاسيكي. كانت هناك دروس جيدة حول OAuth2 . ولم يكن هناك سوى القليل من المعلومات الجنائية حول كيفية الجمع بين الطريقتين. أثناء عملية البحث، تمكنا من التوصل إلى حل عملي. إنها لا تدعي أنها الحقيقة المطلقة، ولكنها تؤدي وظيفتها. سأوضح في هذه المقالة كيفية تنفيذ خدمة تخزين الملاحظات بتكوين Spring Security مماثل من البداية. دعنا نقدم تسجيل الدخول المنتظم عبر البريد الإلكتروني وOAuth2 إلى Spring Security باستخدام مثال خدمة الملاحظات - 1ملاحظة: من الجيد أن يكون القارئ قد اجتاز ما لا يقل عن اثنين من البرامج التعليمية حول Spring، لأن الاهتمام سيتركز فقط على Spring Security، دون شرح مفصل للمستودعات ووحدات التحكم وما إلى ذلك. وإلا، فقد تتحول مقالة كبيرة بالفعل إلى كن عملاقا. محتوى
  1. إنشاء مشروع
  2. إنشاء الكيانات ومنطق التطبيق
    1. جهات
    2. المستودعات
    3. وحدات التحكم
    4. الصفحات
  3. تكوين Spring Security لتسجيل الدخول الكلاسيكي
    1. التكوين الأساسي SecurityConfig
    2. تسجيل دخول المستخدم المخصص
    3. دعونا تحسين وحدة التحكم
    4. يطلق
  4. إعداد OAuth2 باستخدام Google كمثال في Spring Security
    1. تكوين التصفية وapplication.properties
    2. أبرز مميزات تسجيل تطبيق في Google Cloud Platform
    3. CustomUserInfoTokenServices
  5. الإطلاق النهائي للمشروع

إنشاء مشروع

نذهب إلى start.spring.io ونشكل أساس المشروع:
  • الويب - إطلاق تطبيق على Tomcat المدمج، وتعيينات عناوين URL وما شابه؛
  • JPA - اتصال قاعدة البيانات؛
  • Moustache هو محرك قالب يستخدم لإنشاء صفحات الويب؛
  • الأمن - حماية التطبيق. هذا هو ما تم إنشاء هذه المقالة ل.
قم بتنزيل الأرشيف الناتج وفك ضغطه في المجلد الذي تحتاجه. نطلقه في IDE. يمكنك اختيار قاعدة البيانات حسب تقديرك. أستخدم MySQL كقاعدة بيانات للمشروع، لذا أقوم بإضافة التبعية التالية إلى ملف pom.xml في كتلة <dependeency>:
<dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>5.1.34</version>
</dependency>
تكوين application.properties حاليًا كما يلي:
spring.datasource.url=jdbc:mysql://localhost:3306/springsectut?createDatabaseIfNotExist=true&useSSL=false&autoReconnect=true&useLegacyDatetimeCode=false&serverTimezone=UTC&useUnicode=yes&characterEncoding=UTF-8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=yourUsername
spring.datasource.password=yourPassword

spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update

spring.jpa.properties.connection.characterEncoding=utf-8
spring.jpa.properties.connection.CharSet=utf-8
spring.jpa.properties.connection.useUnicode=true

spring.mustache.expose-request-attributes=true

إنشاء الكيانات ومنطق التطبيق

جهات

لنقم بإنشاء حزمة entitiesنضع فيها كيانات قاعدة البيانات. سيتم وصف المستخدم من خلال فئة Userتنفذ الواجهة UserDetails، والتي ستكون ضرورية لتكوين Spring Security. سيكون لدى المستخدم معرف واسم مستخدم (هذا هو البريد الإلكتروني) وكلمة المرور والاسم والدور وعلامة النشاط واسم حساب Google والبريد الإلكتروني ( googleNameو googleUsername).
@Entity
@Table(name = "user")
public class User implements UserDetails
{
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;

  private String username;
  private String password;
  private String name;
  private boolean active;
  private String googleName;
  private String googleUsername;

  @ElementCollection(targetClass = Role.class, fetch = FetchType.EAGER)
  @CollectionTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"))
  @Enumerated(EnumType.STRING)
  private Set<Role> roles;

    //Геттеры, сеттеры, toString(), equals(), hashcode(), имплементация UserDetails
}
تُستخدم أدوار المستخدم لتنظيم الوصول في Spring Security. سيستخدم تطبيقنا دورًا واحدًا فقط:
public enum Role implements GrantedAuthority
{
  USER;

  @Override
  public String getAuthority()
  {
     return name();
  }
}
لنقم بإنشاء فئة ملاحظة بالمعرف وعنوان الملاحظة ونص الملاحظة ومعرف المستخدم الذي تنتمي إليه:
@Entity
@Table(name = "note")
public class Note
{
  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  private Long id;

  private String title;
  private String note;
  private Long userId;

    //Геттеры, сеттеры, toString(), equals(), hashcode()
}

المستودعات

لحفظ الكيانات في قاعدة البيانات، نحتاج إلى مستودعات تقوم بكل الأعمال القذرة نيابةً عنا. لنقم بإنشاء حزمة repos، وسنقوم فيها بإنشاء واجهات UserRepoموروثة NoteRepoمن الواجهة JpaRepository<Entity, Id>.
@Service
@Repository
public interface UserRepo extends JpaRepository<User, Long>
{}

@Service
@Repository
public interface NoteRepo extends JpaRepository<Note, Long>
{
  List<Note> findByUserId(Long userId);
}

وحدات التحكم

ستحتوي خدمة الملاحظات لدينا على الصفحات التالية:
  • بيت؛
  • تسجيل؛
  • مدخل؛
  • قائمة ملاحظات المستخدم.
يجب أن يكون للمستخدم المصرح له فقط حق الوصول إلى قائمة الملاحظات. الصفحات المتبقية عامة. لنقم بإنشاء حزمة controllersتحتوي على فئة IndexControllerتحتوي على التعيين المعتاد للصفحة الرئيسية. الفصل RegistrationControllerهو المسؤول عن تسجيل المستخدم. يأخذ ما بعد التعيين البيانات من النموذج، ويحفظ المستخدم في قاعدة البيانات ويعيد التوجيه إلى صفحة تسجيل الدخول. PasswordEncoderسيتم وصفها لاحقا. يتم استخدامه لتشفير كلمات المرور.
@Controller
public class RegistrationController
{
  @Autowired
  private UserRepo userRepo;

  @Autowired
  private PasswordEncoder passwordEncoder;

  @GetMapping("/registration")
  public String registration()
  {
     return "registration";
  }

  @PostMapping("/registration")
  public String addUser(String name, String username, String password)
  {
     User user = new User();
     user.setName(name);
     user.setUsername(username);
     user.setPassword(passwordEncoder.encode(password));
     user.setActive(true);
     user.setRoles(Collections.singleton(Role.USER));

     userRepo.save(user);

     return "redirect:/login";
  }
تحتوي وحدة التحكم المسؤولة عن صفحة قائمة الملاحظات حاليًا على وظائف مبسطة، والتي ستصبح أكثر تعقيدًا بعد تطبيق Spring Security.
@Controller
public class NoteController
{
  @Autowired
  private NoteRepo noteRepo;

  @GetMapping("/notes")
  public String notes(Model model)
  {
     List<Note> notes = noteRepo.findAll();
     model.addAttribute("notes", notes);

     return "notes";
  }

  @PostMapping("/addnote")
  public String addNote(String title, String note)
  {
     Note newNote = new Note();
     newNote.setTitle(title);
     newNote.setNote(note);

     noteRepo.save(newNote);

     return "redirect:/notes";
  }
}
لن نكتب وحدة تحكم لصفحة تسجيل الدخول لأنها مستخدمة بواسطة Spring Security. بدلا من ذلك، سوف نحتاج إلى تكوين خاص. كالعادة، لنقم بإنشاء حزمة أخرى، ونسميها config، ونضع الفصل هناك MvcConfig. عندما نكتب تكوين Spring Security، سيعرف الصفحة التي نشير إليها عندما نستخدم "/login".
@Configuration
public class MvcConfig implements WebMvcConfigurer
{
  public void addViewControllers(ViewControllerRegistry registry)
  {
     registry.addViewController("/login").setViewName("login");
     registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
  }
}

الصفحات

أستخدم محرك قالب Moustache لإنشاء الصفحات . يمكنك تنفيذ واحدة أخرى، لا يهم. تم إنشاء ملف meta.mustache لمعلومات التعريف المستخدمة في كافة الصفحات. ويتضمن أيضًا Bootstrap لجعل صفحات مشروعنا تبدو أجمل. يتم إنشاء الصفحات في الدليل "src/main/resources/templates". الملفات لها شارب التمديد. سيؤدي وضع كود html مباشرة في المقالة إلى جعلها كبيرة جدًا، لذا إليك رابط لمجلد القوالب في مستودع GitHub الخاص بالمشروع .

تكوين Spring Security لتسجيل الدخول الكلاسيكي

يساعدنا Spring Security على حماية التطبيق وموارده من الوصول غير المصرح به. سوف نقوم بإنشاء تكوين عمل موجز في فئة SecurityConfigموروثة منها WebSecurityConfigurerAdapter، والتي سنضعها في الحزمة config. لنضع علامة عليها باستخدام التعليق التوضيحي @EnableWebSecurity، والذي سيمكن دعم Spring Security، والتعليق التوضيحي @Configuration، الذي يشير إلى أن هذه الفئة تحتوي على بعض التكوينات. ملاحظة: يحتوي ملف pom.xml الذي تم تكوينه تلقائيًا على نسخة من المكون الأصلي لـ Spring Boot 2.1.4.RELEASE، مما حال دون تنفيذ الأمان بالطريقة المحددة. لتجنب التعارضات في المشروع، يوصى بتغيير الإصدار إلى 2.0.1.RELEASE.

التكوين الأساسي SecurityConfig

سيكون التكوين الخاص بنا قادرًا على:
  1. تشفير كلمات المرور باستخدام BCryptPasswordEncoder:

    @Autowired
    private PasswordEncoder passwordEncoder;
    
    @Bean
    PasswordEncoder passwordEncoder()
    {
      PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
      return passwordEncoder;
    }
  2. قم بتسجيل الدخول باستخدام مزود مصادقة مكتوب خصيصًا:

    @Autowired
    private AuthProvider authProvider;
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth)
    {
      auth.authenticationProvider(authProvider);
    }
  3. السماح للمستخدمين المجهولين بالوصول إلى الصفحة الرئيسية وصفحات التسجيل وتسجيل الدخول. يجب تنفيذ كافة الطلبات الأخرى من قبل المستخدمين الذين قاموا بتسجيل الدخول. لنقم بتعيين "/ تسجيل الدخول" الموصوف مسبقًا كصفحة تسجيل الدخول. إذا تم تسجيل الدخول بنجاح، فسيتم نقل المستخدم إلى صفحة تحتوي على قائمة بالملاحظات، وإذا كان هناك خطأ، فسيظل المستخدم في صفحة تسجيل الدخول. عند الخروج بنجاح، سيتم نقل المستخدم إلى الصفحة الرئيسية.

    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
      http
            .authorizeRequests()
            .antMatchers("/resources/**", "/", "/login**", "/registration").permitAll()
            .anyRequest().authenticated()
            .and().formLogin().loginPage("/login")
            .defaultSuccessUrl("/notes").failureUrl("/login?error").permitAll()
            .and().logout().logoutSuccessUrl("/").permitAll();
    }

تسجيل دخول المستخدم المخصص

ستسمح الرسالة المكتوبة ذاتيًا AuthProviderللمستخدم بتسجيل الدخول ليس فقط عن طريق البريد الإلكتروني، ولكن أيضًا عن طريق اسم المستخدم.
@Component
public class AuthProvider implements AuthenticationProvider
{
  @Autowired
  private UserService userService;

  @Autowired
  private PasswordEncoder passwordEncoder;

  public Authentication authenticate(Authentication authentication) throws AuthenticationException
  {
     String username = authentication.getName();
     String password = (String) authentication.getCredentials();

     User user = (User) userService.loadUserByUsername(username);

     if(user != null && (user.getUsername().equals(username) || user.getName().equals(username)))
     {
        if(!passwordEncoder.matches(password, user.getPassword()))
        {
           throw new BadCredentialsException("Wrong password");
        }

        Collection<? extends GrantedAuthority> authorities = user.getAuthorities();

        return new UsernamePasswordAuthenticationToken(user, password, authorities);
     }
     else
        throw new BadCredentialsException("Username not found");
  }

  public boolean supports(Class<?> arg)
  {
     return true;
  }
}
كما لاحظت، فإن الفئة UserServiceالموجودة في الحزمة هي المسؤولة عن تحميل المستخدم services. في حالتنا، فإنه يبحث عن مستخدم ليس فقط عن طريق الحقل username، مثل التنفيذ المضمن، ولكن أيضًا عن طريق اسم المستخدم واسم حساب Google والبريد الإلكتروني لحساب Google. ستكون الطريقتان الأخيرتان مفيدتين لنا عند تنفيذ تسجيل الدخول عبر OAuth2. هنا يتم إعطاء الفصل في نسخة مختصرة.
@Service
public class UserService implements UserDetailsService
{
  @Autowired
  private UserRepo userRepo;

  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
  {
     User userFindByUsername = userRepo.findByUsername(username);
     //Остальные поиски

     if(userFindByUsername != null)
     {
        return userFindByUsername;
     }
     //Остальные проверки
     return null;
  }
}
ملحوظة: لا تنس أن تكتب الطرق اللازمة في UserRepo!

دعونا تحسين وحدة التحكم

لقد قمنا بتكوين Spring Security. الآن هو الوقت المناسب للاستفادة من هذا في وحدة تحكم الملاحظات الخاصة بك. الآن سيقبل كل تعيين معلمة رئيسية إضافية، والتي سيحاول من خلالها العثور على المستخدم. لماذا لا يمكنني حقن الفصل مباشرة User؟ ثم سيكون هناك تعارض بسبب عدم تطابق أنواع المستخدمين عندما نكتب تسجيل دخول عبر الشبكات الاجتماعية. نحن نقدم المرونة اللازمة مقدما. يبدو الآن رمز وحدة التحكم في الملاحظات كما يلي:
@GetMapping("/notes")
public String notes(Principal principal, Model model)
{
  User user = (User) userService.loadUserByUsername(principal.getName());
  List<Note> notes = noteRepo.findByUserId(user.getId());
  model.addAttribute("notes", notes);
  model.addAttribute("user", user);

  return "notes";
}

@PostMapping("/addnote")
public String addNote(Principal principal, String title, String note)
{
  User user = (User) userService.loadUserByUsername(principal.getName());

  Note newNote = new Note();
  newNote.setTitle(title);
  newNote.setNote(note);
  newNote.setUserId(user.getId());

  noteRepo.save(newNote);

  return "redirect:/notes";
}
ملحوظة: تم تمكين حماية CSRF افتراضيًا للمشروع ، لذا قم بتعطيلها بنفسك (http.csrf().disable())، أو لا تنس، كمؤلف المقالة، إضافة حقل مخفي باستخدام رمز csrf لجميع طلبات النشر.

يطلق

نحن نحاول إطلاق المشروع.
دعنا نقدم تسجيل الدخول المنتظم عبر البريد الإلكتروني وOAuth2 إلى Spring Security باستخدام مثال خدمة الملاحظات - 1
دعنا نقدم تسجيل الدخول المنتظم عبر البريد الإلكتروني وOAuth2 إلى Spring Security باستخدام مثال خدمة الملاحظات - 2
نرى أن مستخدمًا جديدًا قد ظهر في قاعدة البيانات. كلمة المرور مشفرة.
دعنا نقدم تسجيل الدخول المنتظم عبر البريد الإلكتروني وOAuth2 إلى Spring Security باستخدام مثال خدمة الملاحظات - 3
دعنا نقدم تسجيل الدخول المنتظم عبر البريد الإلكتروني وOAuth2 إلى Spring Security باستخدام مثال خدمة الملاحظات - 4
دعنا نقدم تسجيل الدخول المنتظم عبر البريد الإلكتروني وOAuth2 إلى Spring Security باستخدام مثال خدمة الملاحظات - 5
دعنا نقدم تسجيل الدخول المنتظم عبر البريد الإلكتروني وOAuth2 إلى Spring Security باستخدام مثال خدمة الملاحظات - 6
يتم حفظ الملاحظات في قاعدة البيانات.
دعنا نقدم تسجيل الدخول المنتظم عبر البريد الإلكتروني وOAuth2 إلى Spring Security باستخدام مثال خدمة الملاحظات - 7
نرى أن المشروع تم إطلاقه وتشغيله بنجاح. للحصول على السعادة الكاملة، نحتاج فقط إلى القدرة على تسجيل الدخول عبر الشبكات الاجتماعية. حسنا، دعونا نبدأ!

إعداد OAuth2 باستخدام Google كمثال في Spring Security

عند تنفيذ OAuth2، اعتمدت على هذا البرنامج التعليمي الرسمي من Spring . لدعم OAuth2، أضف المكتبة التالية إلى pom.xml:
<dependency>
  <groupId>org.springframework.security.oauth.boot</groupId>
  <artifactId>spring-security-oauth2-autoconfigure</artifactId>
  <version>2.0.0.RELEASE</version>
</dependency>
دعونا نعدل تكوين Spring Security الخاص بنا في ملف SecurityConfig. أولاً، دعونا نضيف التعليق التوضيحي @EnableOAuth2Client. سيقوم تلقائيًا بسحب ما تحتاجه لتسجيل الدخول عبر الشبكات الاجتماعية.

تكوين التصفية وapplication.properties

لنقم بإدخال OAuth2ClientContext لاستخدامه في تكوين الأمان الخاص بنا.
@Autowired
private OAuth2ClientContext oAuth2ClientContext;
يتم استخدام OAuth2ClientContext عند إنشاء عامل تصفية للتحقق من صحة طلب تسجيل الدخول الاجتماعي للمستخدم. يتوفر الفلتر بفضل التعليق التوضيحي @EnableOAuth2Client. كل ما يتعين علينا فعله هو الاتصال بالترتيب الصحيح، قبل مرشح Spring Security الرئيسي. عندها فقط سنكون قادرين على التقاط عمليات إعادة التوجيه أثناء عملية تسجيل الدخول باستخدام OAuth2. للقيام بذلك، نستخدم FilterRegistrationBean، حيث قمنا بتعيين أولوية مرشحنا على -100.
@Bean
public FilterRegistrationBean oAuth2ClientFilterRegistration(OAuth2ClientContextFilter oAuth2ClientContextFilter)
{
  FilterRegistrationBean registration = new FilterRegistrationBean();
  registration.setFilter(oAuth2ClientContextFilter);
  registration.setOrder(-100);
  return registration;
}

private Filter ssoFilter()
{
  OAuth2ClientAuthenticationProcessingFilter googleFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/google");
  OAuth2RestTemplate googleTemplate = new OAuth2RestTemplate(google(), oAuth2ClientContext);
  googleFilter.setRestTemplate(googleTemplate);
  CustomUserInfoTokenServices tokenServices = new CustomUserInfoTokenServices(googleResource().getUserInfoUri(), google().getClientId());
  tokenServices.setRestTemplate(googleTemplate);
  googleFilter.setTokenServices(tokenServices);
  tokenServices.setUserRepo(userRepo);
  tokenServices.setPasswordEncoder(passwordEncoder);
  return googleFilter;
}
تحتاج أيضًا إلى إضافة عامل تصفية جديد إلى وظيفة التكوين (HttpSecurity http):
http.addFilterBefore(ssoFilter(), UsernamePasswordAuthenticationFilter.class);
يحتاج الفلتر أيضًا إلى معرفة أن العميل قد تم تسجيله عبر Google. يحدد التعليق التوضيحي @ConfigurationProperties خصائص التكوين التي يجب البحث عنها في application.properties.
@Bean
@ConfigurationProperties("google.client")
public AuthorizationCodeResourceDetails google()
{
  return new AuthorizationCodeResourceDetails();
}
لإكمال المصادقة، تحتاج إلى تحديد نقطة نهاية معلومات مستخدم Google:
@Bean
@ConfigurationProperties("google.resource")
public ResourceServerProperties googleResource()
{
  return new ResourceServerProperties();
}
بعد تسجيل تطبيقنا في Google Cloud Platform ، سنضيف خصائص بالبادئات المناسبة إلى application.properties:
google.client.clientId=yourClientId
google.client.clientSecret=yourClientSecret
google.client.accessTokenUri=https://www.googleapis.com/oauth2/v4/token
google.client.userAuthorizationUri=https://accounts.google.com/o/oauth2/v2/auth
google.client.clientAuthenticationScheme=form
google.client.scope=openid,email,profile
google.resource.userInfoUri=https://www.googleapis.com/oauth2/v3/userinfo
google.resource.preferTokenInfo=true

أبرز مميزات تسجيل تطبيق في Google Cloud Platform

المسار: واجهات برمجة التطبيقات والخدمات -> نافذة طلب الوصول إلى بيانات الاعتماد عبر OAuth:
  • اسم التطبيق: نموذج تسجيل الدخول الربيعي والبرنامج التعليمي لـ OAuth2
  • دعم عنوان البريد الإلكتروني: البريد الإلكتروني الخاص بك
  • نطاق Google API: البريد الإلكتروني، الملف الشخصي، openid
  • النطاقات المعتمدة: me.org
  • رابط الصفحة الرئيسية للتطبيق: http://me.org:8080
  • رابط سياسة خصوصية التطبيق: http://me.org:8080
  • رابط شروط استخدام التطبيق: http://me.org:8080
أوراق اعتماد:
  • النوع: تطبيق ويب
  • العنوان: نموذج تسجيل الدخول الربيعي والبرنامج التعليمي لـ OAuth2
  • مصادر جافا سكريبت المسموح بها: http://me.org، http://me.org:8080
  • عناوين URI المسموح بها لإعادة التوجيه: http://me.org:8080/login، http://me.org:8080/login/google
ملاحظة: بما أن Google لا تريد العمل مع العنوان localhost:8080، أضف السطر "127.0.0.1 me.org" أو شيء مشابه للملف C:\Windows\System32\drivers\etc\hosts في النهاية. الشيء الرئيسي هو أن المجال في شكل كلاسيكي.

CustomUserInfoTokenServices

هل لاحظت كلمة "مخصص" في وصف وظيفة الفلتر؟ فصل CustomUserInfoTokenServices. نعم، سوف نقوم بإنشاء فصلنا الخاص مع لعبة البلاك جاك والقدرة على حفظ المستخدم في قاعدة البيانات! باستخدام اختصار لوحة المفاتيح Ctrl-N في IntelliJ IDEA، يمكنك العثور على كيفية UserInfoTokenServicesتنفيذ الإعداد الافتراضي ورؤيته. لننسخ الكود الخاص به إلى الفصل الذي تم إنشاؤه حديثًا CustomUserInfoTokenServices. يمكن ترك معظمها دون تغيير. قبل تغيير منطق الوظائف، دعونا نضيف UserRepoوحقول خاصة للفئة PasswordEncoder. دعونا ننشئ محددات لهم. دعونا نضيف @Autowired UserRepo userRepo إلى فئة SecurityConfig. نحن ننظر إلى كيفية اختفاء مؤشر الخطأ في طريقة إنشاء الفلتر ونبتهج. لماذا لا يمكن تطبيق @Autowired مباشرة على CustomUserInfoTokenServices؟ لأن هذه الفئة لن تلتقط التبعية، لأنها نفسها لم يتم تمييزها بأي تعليق توضيحي لـ Spring، ويتم إنشاء مُنشئها بشكل صريح عند الإعلان عن الفلتر. وبناءً على ذلك، فإن آلية Spring's DI لا تعرف شيئًا عن ذلك. إذا قمنا بتعليق @Autowired على أي شيء في هذه الفئة، فسنحصل على NullPointerException عند استخدامه. ولكن من خلال أدوات الضبط الصريحة، كل شيء يعمل بشكل جيد للغاية. بعد تنفيذ المكونات الضرورية، يصبح الكائن الرئيسي محل الاهتمام هو وظيفة LoadAuthentication، حيث يتم استرداد Map<String, Object> بمعلومات حول المستخدم. في هذا المشروع قمت بتنفيذ حفظ المستخدم الذي قام بتسجيل الدخول عبر شبكة اجتماعية إلى قاعدة البيانات. نظرًا لأننا نستخدم حساب Google كموفر OAuth2، فإننا نتحقق مما إذا كانت الخريطة تحتوي على الحقل "الفرعي" الذي يعتبر نموذجيًا بالنسبة لـ Google. إذا كان موجودا، فهذا يعني أنه تم استلام المعلومات المتعلقة بالمستخدم بشكل صحيح. نقوم بإنشاء مستخدم جديد وحفظه في قاعدة البيانات.
@Override
public OAuth2Authentication loadAuthentication(String accessToken)
     throws AuthenticationException, InvalidTokenException
{
  Map<String, Object> map = getMap(this.userInfoEndpointUrl, accessToken);

  if(map.containsKey("sub"))
  {
     String googleName = (String) map.get("name");
     String googleUsername = (String) map.get("email");

     User user = userRepo.findByGoogleUsername(googleUsername);

     if(user == null)
     {
        user = new User();
        user.setActive(true);
        user.setRoles(Collections.singleton(Role.USER));
     }

     user.setName(googleName);
     user.setUsername(googleUsername);
     user.setGoogleName(googleName);
     user.setGoogleUsername(googleUsername);
     user.setPassword(passwordEncoder.encode("oauth2user"));

     userRepo.save(user);
  }

  if (map.containsKey("error"))
  {
     this.logger.debug("userinfo returned error: " + map.get("error"));
     throw new InvalidTokenException(accessToken);
  }
  return extractAuthentication(map);
}
عند استخدام عدة موفري خدمات، يمكنك تحديد خيارات مختلفة في CustomUserInfoTokenServices واحد، وتسجيل فئات مختلفة من الخدمات المتشابهة في طريقة تعريف عامل التصفية. الآن يمكن لكل من المستخدم وOAuth2Authentication أن يعملا كمديرين. وبما أننا أخذنا في الاعتبار في UserService تحميل المستخدم من خلال بيانات Google مسبقًا، فإن التطبيق سيعمل لكلا النوعين من المستخدمين. نقوم بتعديل وحدة تحكم الصفحة الرئيسية للمشروع بحيث تعيد توجيه المستخدمين الذين قاموا بتسجيل الدخول باستخدام OAuth2 إلى صفحة الملاحظات.
@GetMapping("/")
public String index(Principal principal)
{
  if(principal != null)
  {
     return "redirect:/notes";
  }
  return "index";
}

الإطلاق النهائي للمشروع

بعد إجراء تغييرات تجميلية طفيفة وإضافة زر الخروج، نقوم بالإطلاق النهائي للمشروع.
دعنا نقدم تسجيل الدخول المنتظم عبر البريد الإلكتروني وOAuth2 إلى Spring Security باستخدام مثال خدمة الملاحظات - 8
دعنا نقدم تسجيل الدخول المنتظم عبر البريد الإلكتروني وOAuth2 إلى Spring Security باستخدام مثال خدمة الملاحظات - 9
دعنا نقدم تسجيل الدخول المنتظم عبر البريد الإلكتروني وOAuth2 إلى Spring Security باستخدام مثال خدمة الملاحظات - 10
دعنا نقدم تسجيل الدخول المنتظم عبر البريد الإلكتروني وOAuth2 إلى Spring Security باستخدام مثال خدمة الملاحظات - 11
دعنا نقدم تسجيل الدخول المنتظم عبر البريد الإلكتروني وOAuth2 إلى Spring Security باستخدام مثال خدمة الملاحظات - 12
دعنا نقدم تسجيل الدخول المنتظم عبر البريد الإلكتروني وOAuth2 إلى Spring Security باستخدام مثال خدمة الملاحظات - 13
دعنا نقدم تسجيل الدخول المنتظم عبر البريد الإلكتروني وOAuth2 إلى Spring Security باستخدام مثال خدمة الملاحظات - 14
دعنا نقدم تسجيل الدخول المنتظم عبر البريد الإلكتروني وOAuth2 إلى Spring Security باستخدام مثال خدمة الملاحظات - 15
يقوم المستخدم بتسجيل الدخول بنجاح من خلال النموذج العادي ومن خلال حساب Google. هذا هو ما أردنا! آمل أن تكون هذه المقالة قد أوضحت بعض النقاط حول إنشاء تطبيق ويب وتأمينه باستخدام Spring Security والجمع بين طرق تسجيل الدخول المختلفة. مع رمز المشروع الكامل يمكنك
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION