JavaRush /จาวาบล็อก /Random-TH /เรามาแนะนำการเข้าสู่ระบบปกติผ่านอีเมลและ OAuth2 ให้กับ Sp...

เรามาแนะนำการเข้าสู่ระบบปกติผ่านอีเมลและ OAuth2 ให้กับ Spring Security โดยใช้ตัวอย่างบริการ Notes

เผยแพร่ในกลุ่ม
ในขณะที่เขียนใบสมัคร ฉันต้องเผชิญกับการขาดบทความที่ชัดเจนเกี่ยวกับวิธีให้ผู้ใช้ลงทะเบียนทั้งทางอีเมลและโซเชียลเน็ตเวิร์ก มีบทช่วยสอนที่ดีเกี่ยวกับการตั้งค่าแบบฟอร์มการเข้าสู่ระบบแบบคลาสสิก มีบทช่วยสอนที่ดีเกี่ยวกับ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 ในบล็อก <การพึ่งพา>:
<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 ผู้ใช้จะมี ID, ชื่อผู้ใช้ (นี่คืออีเมล), รหัสผ่าน, ชื่อ, บทบาท, ธงกิจกรรม, ชื่อบัญชี 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();
  }
}
มาสร้างคลาสบันทึกด้วย id, ชื่อบันทึก, เนื้อหาบันทึก และ id ของผู้ใช้ที่เป็นเจ้าของ:
@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);
  }
}

หน้า

ฉันใช้ เครื่องมือเทมเพลตหนวดเพื่อสร้างเพจ คุณสามารถนำไปใช้อย่างอื่นได้ไม่สำคัญ ไฟล์ 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. อนุญาตให้ผู้ใช้ที่ไม่ระบุชื่อเข้าถึงหน้าแรก หน้าลงทะเบียน และหน้าเข้าสู่ระบบ คำขออื่นๆ ทั้งหมดจะต้องดำเนินการโดยผู้ใช้ที่เข้าสู่ระบบ มากำหนด "/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 Security แล้ว ตอนนี้เป็นเวลาที่จะใช้ประโยชน์จากสิ่งนี้ในตัวควบคุมบันทึกย่อของคุณ ตอนนี้การแมปแต่ละครั้งจะยอมรับพารามิเตอร์ Principal เพิ่มเติม ซึ่งจะพยายามค้นหาผู้ใช้ เหตุใดฉันจึงไม่สามารถฉีดคลาสโดยตรง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แล้ว เราจะเพิ่มคุณสมบัติด้วยคำนำหน้าที่เหมาะสมกับ 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 ข้อมูลประจำตัว:
  • ชื่อแอปพลิเคชัน:แบบฟอร์มเข้าสู่ระบบ Spring และบทช่วยสอน OAuth2
  • ที่อยู่อีเมลสนับสนุน:อีเมลของคุณ
  • ขอบเขต Google API:อีเมล โปรไฟล์ openid
  • โดเมนที่ได้รับอนุญาต: me.org
  • ลิงก์ไปยังหน้าหลักของแอปพลิเคชัน: http://me.org:8080
  • ลิงก์ไปยังนโยบายความเป็นส่วนตัวของแอป: http://me.org:8080
  • ลิงก์ไปยังข้อกำหนดการใช้งาน: http://me.org:8080
ข้อมูลรับรอง:
  • ประเภท:เว็บแอปพลิเคชัน
  • หัวข้อ:แบบฟอร์มเข้าสู่ระบบ Spring และบทช่วยสอน OAuth2
  • แหล่งที่มาของ JavaScript ที่อนุญาต: 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

คุณสังเกตเห็นคำว่า Custom ในคำอธิบายฟังก์ชันตัวกรองหรือไม่ ระดับCustomUserInfoTokenServices. ใช่ เราจะสร้างคลาสของเราเองด้วยแบล็คแจ็คและความสามารถในการบันทึกผู้ใช้ในฐานข้อมูล! การใช้แป้นพิมพ์ลัด Ctrl-N ใน IntelliJ IDEA คุณสามารถค้นหาและดูว่าค่าUserInfoTokenServicesเริ่มต้นถูกนำไปใช้ อย่างไร มาคัดลอกโค้ดของมันลงในคลาสที่สร้างขึ้นCustomUserInfoTokenServicesใหม่ ส่วนใหญ่สามารถปล่อยให้ไม่เปลี่ยนแปลงได้ ก่อนที่จะเปลี่ยนลอจิกของฟังก์ชัน มาเพิ่มUserRepoและ เป็นฟิลด์ส่วนตัวของคลาสกันดี PasswordEncoderกว่า มาสร้างเซ็ตเตอร์ให้พวกเขากันเถอะ มาเพิ่ม @Autowired UserRepo userRepo ให้กับคลาส SecurityConfig เราดูว่าตัวชี้ไปยังข้อผิดพลาดในวิธีสร้างตัวกรองหายไปอย่างไรและเราชื่นชมยินดี เหตุใดจึงไม่สามารถใช้ @Autowired กับ CustomUserInfoTokenServices ได้โดยตรง เนื่องจากคลาสนี้จะไม่รับการขึ้นต่อกัน เนื่องจากตัวมันเองไม่ได้ทำเครื่องหมายด้วยคำอธิบายประกอบ Spring ใด ๆ และตัวสร้างของคลาสจะถูกสร้างขึ้นอย่างชัดเจนเมื่อมีการประกาศตัวกรอง ดังนั้นกลไก DI ของ Spring จึงไม่ทราบเรื่องนี้ หากเราใส่คำอธิบายประกอบ @Autowired บนสิ่งใดๆ ในคลาสนี้ เราจะได้รับ NullPointerException เมื่อใช้ แต่ทุกอย่างทำงานได้ดีมากผ่านผู้ตั้งค่าที่ชัดเจน หลังจากใช้ส่วนประกอบที่จำเป็นแล้ว ออบเจ็กต์หลักที่น่าสนใจจะกลายเป็นฟังก์ชัน loadAuthentication ซึ่งแมป<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 เดียว และลงทะเบียนคลาสที่แตกต่างกันของบริการที่คล้ายกันในวิธีการประกาศตัวกรอง ขณะนี้ทั้ง User และ OAuth2Authentication สามารถทำหน้าที่เป็น Principal ได้ เนื่องจากเราคำนึงถึงการโหลดผู้ใช้ผ่านข้อมูล Google ล่วงหน้าใน UserService แอปพลิเคชันจึงทำงานสำหรับผู้ใช้ทั้งสองประเภท เราแก้ไขตัวควบคุมหน้าหลักของโครงการเพื่อให้เปลี่ยนเส้นทางผู้ใช้ที่เข้าสู่ระบบโดยใช้ 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