ในขณะที่เขียนใบสมัคร ฉันต้องเผชิญกับการขาดบทความที่ชัดเจนเกี่ยวกับวิธีให้ผู้ใช้ลงทะเบียนทั้งทางอีเมลและโซเชียลเน็ตเวิร์ก มีบทช่วยสอนที่ดีเกี่ยวกับการตั้งค่าแบบฟอร์มการเข้าสู่ระบบแบบคลาสสิก มีบทช่วยสอนที่ดีเกี่ยวกับOAuth2 มีข้อมูลทางอาญาเพียงเล็กน้อยเกี่ยวกับวิธีการรวมทั้งสองวิธีเข้าด้วยกัน ในระหว่างกระบวนการค้นหา เราก็สามารถคิดวิธีแก้ปัญหาที่ใช้การได้ มันไม่ได้อ้างว่าเป็นความจริงขั้นสุดท้าย แต่ทำหน้าที่ของมันให้สำเร็จ ในบทความนี้ ฉันจะแสดงวิธีการใช้บริการพื้นที่เก็บข้อมูลบันทึกย่อด้วยการกำหนดค่า Spring Security ที่คล้ายกันตั้งแต่เริ่มต้น
หมายเหตุ:เป็นเรื่องดีหากผู้อ่านได้อ่านบทช่วยสอนเกี่ยวกับ Spring อย่างน้อยสองสามบท เนื่องจากความสนใจจะเน้นไปที่ Spring Security เท่านั้น โดยไม่มีคำอธิบายโดยละเอียดเกี่ยวกับที่เก็บ ตัวควบคุม ฯลฯ มิฉะนั้น บทความที่มีขนาดค่อนข้างใหญ่อยู่แล้วจะกลายเป็น มีขนาดมหึมา เนื้อหา
เราเห็นว่ามีผู้ใช้ใหม่ปรากฏในฐานข้อมูล รหัสผ่านถูกเข้ารหัส
บันทึกย่อจะถูกบันทึกลงในฐานข้อมูล
เราเห็นว่าโครงการเปิดตัวและดำเนินการได้สำเร็จ เพื่อความสุขที่สมบูรณ์ เราต้องการเพียงความสามารถในการเข้าสู่ระบบผ่านโซเชียลเน็ตเวิร์กเท่านั้น เอาล่ะ มาเริ่มกันเลย!
ขณะนี้ทั้ง User และ OAuth2Authentication สามารถทำหน้าที่เป็น Principal ได้ เนื่องจากเราคำนึงถึงการโหลดผู้ใช้ผ่านข้อมูล Google ล่วงหน้าใน UserService แอปพลิเคชันจึงทำงานสำหรับผู้ใช้ทั้งสองประเภท เราแก้ไขตัวควบคุมหน้าหลักของโครงการเพื่อให้เปลี่ยนเส้นทางผู้ใช้ที่เข้าสู่ระบบโดยใช้ OAuth2 ไปยังหน้าบันทึกย่อ
ผู้ใช้เข้าสู่ระบบได้สำเร็จทั้งผ่านแบบฟอร์มปกติและผ่านบัญชี Google นี่คือสิ่งที่เราต้องการ! ฉันหวังว่าบทความนี้จะช่วยชี้แจงประเด็นต่างๆ เกี่ยวกับการสร้างเว็บแอปพลิเคชัน การรักษาความปลอดภัยด้วย Spring Security และการรวมวิธีการเข้าสู่ระบบต่างๆ ด้วยรหัสโครงการแบบเต็มที่คุณสามารถทำได้
- การสร้างโครงการ
- การสร้างเอนทิตีและตรรกะของแอปพลิเคชัน
- การกำหนดค่า Spring Security สำหรับการเข้าสู่ระบบแบบคลาสสิก
- การตั้งค่า OAuth2 โดยใช้ Google เป็นตัวอย่างใน Spring Security
- การกำหนดค่าตัวกรองและ application.properties
- จุดเด่นของการลงทะเบียนแอปพลิเคชันกับ Google Cloud Platform
- CustomUserInfoTokenServices
- การเปิดตัวโครงการครั้งสุดท้าย
การสร้างโครงการ
เราไปที่start.spring.ioและสร้างพื้นฐานของโครงการ:- เว็บ - การเปิดตัวแอปพลิเคชันบน Tomcat ในตัว การแมป URL และสิ่งที่คล้ายกัน
- JPA - การเชื่อมต่อฐานข้อมูล
- Moustache เป็นเครื่องมือเทมเพลตที่ใช้ในการสร้างหน้าเว็บ
- ความปลอดภัย - การป้องกันแอปพลิเคชัน นี่คือสิ่งที่บทความนี้ถูกสร้างขึ้นเพื่อ
<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
การกำหนดค่าของเราจะสามารถ:-
เข้ารหัสรหัสผ่านโดยใช้
BCryptPasswordEncoder
:@Autowired private PasswordEncoder passwordEncoder; @Bean PasswordEncoder passwordEncoder() { PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); return passwordEncoder; }
-
เข้าสู่ระบบโดยใช้ผู้ให้บริการตรวจสอบสิทธิ์ที่เป็นลายลักษณ์อักษรเป็นพิเศษ:
@Autowired private AuthProvider authProvider; @Override protected void configure(AuthenticationManagerBuilder auth) { auth.authenticationProvider(authProvider); }
-
อนุญาตให้ผู้ใช้ที่ไม่ระบุชื่อเข้าถึงหน้าแรก หน้าลงทะเบียน และหน้าเข้าสู่ระบบ คำขออื่นๆ ทั้งหมดจะต้องดำเนินการโดยผู้ใช้ที่เข้าสู่ระบบ มากำหนด "/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 โดยใช้ 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
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 เดียว และลงทะเบียนคลาสที่แตกต่างกันของบริการที่คล้ายกันในวิธีการประกาศตัวกรอง
@GetMapping("/")
public String index(Principal principal)
{
if(principal != null)
{
return "redirect:/notes";
}
return "index";
}
การเปิดตัวโครงการครั้งสุดท้าย
หลังจากการเปลี่ยนแปลงเล็กน้อยและเพิ่มปุ่มออก เราจะดำเนินการเปิดตัวโครงการครั้งสุดท้าย







GO TO FULL VERSION