JavaRush /وبلاگ جاوا /Random-FA /بیایید با استفاده از مثال سرویس یادداشت، ورود منظم از طری...

بیایید با استفاده از مثال سرویس یادداشت، ورود منظم از طریق ایمیل و OAuth2 را به Spring Security معرفی کنیم.

در گروه منتشر شد
در حین نوشتن درخواستم، با کمبود مقالات واضح در مورد نحوه وادار کردن کاربر به ثبت نام هم از طریق ایمیل و هم از طریق شبکه های اجتماعی مواجه شدم. آموزش های خوبی در مورد راه اندازی فرم ورود کلاسیک وجود داشت. آموزش های خوبی در OAuth2 وجود داشت . اطلاعات جنایی کمی در مورد نحوه ترکیب این دو روش وجود داشت. در طول فرآیند جستجو، ما توانستیم به یک راه حل قابل اجرا برسیم. ادعا نمی کند که حقیقت نهایی است، اما کارکرد خود را انجام می دهد. در این مقاله نحوه پیاده‌سازی سرویس ذخیره‌سازی یادداشت با پیکربندی مشابه Spring Security را از ابتدا نشان خواهم داد. بیایید با استفاده از مثال سرویس یادداشت ها، ورود منظم از طریق ایمیل و OAuth2 را به Spring Security معرفی کنیم - 1توجه: خوب است که خواننده حداقل چند آموزش در مورد Spring را مرور کرده باشد، زیرا توجه فقط بر روی Spring Security متمرکز خواهد شد، بدون توضیحات دقیق در مورد مخازن، کنترلرها و غیره. در غیر این صورت، یک مقاله نسبتاً بزرگ به نظر می رسد. غول پیکر باشد محتوا
  1. ایجاد یک پروژه
  2. ایجاد موجودیت ها و منطق برنامه
    1. موجودیت ها
    2. مخازن
    3. کنترل کننده ها
    4. صفحات
  3. پیکربندی امنیت Spring برای ورود کلاسیک
    1. پیکربندی پایه SecurityConfig
    2. ورود کاربر سفارشی
    3. بیایید کنترلر را بهبود ببخشیم
    4. راه اندازی
  4. راه اندازی OAuth2 با استفاده از Google به عنوان مثال در Spring Security
    1. پیکربندی فیلتر و application.properties
    2. نکات مهم ثبت برنامه در Google Cloud Platform
    3. CustomUserInfoTokenServices
  5. راه اندازی نهایی پروژه

ایجاد یک پروژه

ما به start.spring.io می رویم و اساس پروژه را تشکیل می دهیم:
  • وب - راه اندازی یک برنامه کاربردی در Tomcat داخلی، نگاشت آدرس و موارد مشابه؛
  • JPA - اتصال به پایگاه داده؛
  • Mustache یک موتور قالب است که برای تولید صفحات وب استفاده می شود.
  • امنیت - حفاظت از برنامه. این همان چیزی است که این مقاله برای آن ایجاد شده است.
آرشیو به دست آمده را دانلود کنید و آن را در پوشه مورد نیاز خود باز کنید. ما آن را در IDE راه اندازی می کنیم. شما می توانید پایگاه داده را به صلاحدید خود انتخاب کنید. من از MySQL به عنوان پایگاه داده برای پروژه استفاده می کنم، بنابراین وابستگی زیر را به فایل pom.xml در بلوک <dependencies> اضافه می کنم:
<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به ارث رسیده اند . NoteRepoJpaRepository<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مسئولیت ثبت نام کاربر را بر عهده دارد. Post-mapping داده ها را از فرم می گیرد، کاربر را در پایگاه داده ذخیره می کند و به صفحه ورود هدایت می کند. 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 استفاده می شود. در عوض، ما به یک پیکربندی خاص نیاز خواهیم داشت. طبق معمول، بیایید بسته دیگری ایجاد کنیم، آن را فراخوانی کنیم 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);
  }
}

صفحات

من از موتور قالب Mustache برای ایجاد صفحات استفاده می کنم . شما می توانید یکی دیگر را پیاده سازی کنید، مهم نیست. یک فایل meta.mustache برای اطلاعات متا ایجاد شده است که در تمام صفحات استفاده می شود. همچنین شامل Bootstrap می شود تا صفحات پروژه ما زیباتر به نظر برسند. صفحات در دایرکتوری "src/main/resources/templates" ایجاد می شوند. فایل ها دارای پسوند سبیل هستند. قرار دادن کد html به طور مستقیم در مقاله آن را بیش از حد بزرگ می کند، بنابراین در اینجا پیوندی به پوشه الگوها در مخزن GitHub پروژه وجود دارد .

پیکربندی امنیت Spring برای ورود کلاسیک

Spring Security به ما کمک می کند تا از برنامه و منابع آن در برابر دسترسی غیرمجاز محافظت کنیم. ما یک پیکربندی کاری مختصر در کلاسی که SecurityConfigاز آن به ارث رسیده است ایجاد خواهیم کرد WebSecurityConfigurerAdapterکه آن را در بسته قرار می دهیم config. بیایید آن را با حاشیه‌نویسی EnableWebSecurity@ علامت‌گذاری کنیم، که پشتیبانی Spring Security را فعال می‌کند، و حاشیه‌نویسی @Configuration، که نشان می‌دهد این کلاس دارای تنظیماتی است. توجه: pom.xml پیکربندی شده به صورت خودکار حاوی نسخه مولفه 2.1.4.RELEASE والد Spring Boot بود که مانع از اجرای امنیت به روش تعیین شده می شد. برای جلوگیری از درگیری در پروژه، توصیه می شود نسخه را به 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. به کاربران ناشناس اجازه دسترسی به صفحه اصلی، صفحات ثبت نام و ورود به سیستم را بدهید. تمام درخواست های دیگر باید توسط کاربرانی که وارد سیستم شده اند انجام شود. بیایید «/login» را که قبلاً توضیح داده شد به عنوان صفحه ورود اختصاص دهیم. در صورت موفقیت آمیز بودن ورود، کاربر به صفحه ای با لیستی از یادداشت ها هدایت می شود و در صورت بروز خطا، کاربر در صفحه ورود باقی می ماند. پس از خروج موفقیت آمیز، کاربر به صفحه اصلی منتقل می شود.

    @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 را پیکربندی کرده ایم. اکنون زمان آن است که از این ویژگی در کنترلر یادداشت های خود استفاده کنید. اکنون هر نقشه‌برداری یک پارامتر اصلی اضافی را می‌پذیرد که توسط آن سعی می‌کند کاربر را پیدا کند. چرا نمی توانم مستقیماً کلاس را تزریق کنم 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;
}
همچنین باید یک فیلتر جدید به تابع configure (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

مسیر: APIها و خدمات -> پنجره درخواست دسترسی 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
توجه: از آنجایی که گوگل نمی‌خواهد با آدرس localhost:8080 کار کند، خط "127.0.0.1 me.org" یا چیزی شبیه به فایل C:\Windows\System32\drivers\etc\hosts را در پایان اضافه کنید. نکته اصلی این است که دامنه به شکل کلاسیک است.

CustomUserInfoTokenServices

آیا متوجه کلمه Custom در توضیحات عملکرد فیلتر شده اید؟ کلاس CustomUserInfoTokenServices. بله، ما کلاس خود را با بلک جک و قابلیت ذخیره کاربر در پایگاه داده ایجاد خواهیم کرد! با استفاده از میانبر صفحه کلید Ctrl-N در IntelliJ IDEA، می توانید نحوه UserInfoTokenServicesاجرای پیش فرض را بیابید و ببینید. بیایید کد آن را در کلاس جدید ایجاد شده کپی کنیم CustomUserInfoTokenServices. بیشتر آن را می توان بدون تغییر رها کرد. قبل از تغییر منطق توابع، فیلدهای خصوصی کلاس را اضافه می UserRepoکنیم PasswordEncoder. بیایید تنظیم کننده برای آنها ایجاد کنیم. بیایید @Autowired UserRepo userRepo را به کلاس SecurityConfig اضافه کنیم. ما به نحوه ناپدید شدن نشانگر خطا در روش ایجاد فیلتر نگاه می کنیم و خوشحال می شویم. چرا @Autowired نمی تواند مستقیماً در CustomUserInfoTokenServices اعمال شود؟ زیرا این کلاس وابستگی را دریافت نمی کند، زیرا خود با هیچ حاشیه نویسی Spring علامت گذاری نشده است و سازنده آن به صراحت هنگام اعلان فیلتر ایجاد می شود. بر این اساس، مکانیسم DI اسپرینگ از آن اطلاعی ندارد. اگر @Autowired را روی هر چیزی در این کلاس حاشیه نویسی کنیم، هنگام استفاده یک NullPointerException دریافت خواهیم کرد. اما از طریق تنظیم کننده های صریح همه چیز بسیار خوب کار می کند. پس از پیاده سازی اجزای لازم، شی اصلی مورد علاقه تابع loadAuthentication می شود که در آن Map<String, Object> با اطلاعات کاربر بازیابی می شود. در این پروژه بود که من ذخیره کاربری که از طریق یک شبکه اجتماعی وارد شده بود را در پایگاه داده پیاده سازی کردم. از آنجایی که ما از یک حساب Google به عنوان ارائه‌دهنده OAuth2 استفاده می‌کنیم، بررسی می‌کنیم که آیا نقشه حاوی فیلد «sub» است که برای 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 مشخص کنید و کلاس های مختلفی از خدمات مشابه را در روش اعلام فیلتر ثبت کنید. اکنون هم User و هم OAuth2Authentication می توانند به عنوان Principal عمل کنند. از آنجایی که ما در 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