Ərizəmi yazarkən istifadəçinin həm e-poçt, həm də sosial şəbəkələr vasitəsilə qeydiyyatdan keçməsinə dair aydın məqalələrin çatışmazlığı ilə üzləşdim. Klassik giriş formasının qurulması ilə bağlı yaxşı dərsliklər var idi. OAuth2 -də yaxşı dərsliklər var idi . İki metodun necə birləşdiriləcəyinə dair cinayət baxımından az məlumat var idi. Axtarış prosesində biz işlək həll yolu tapdıq. O, son həqiqət olduğunu iddia etmir, lakin öz funksiyasını yerinə yetirir. Bu yazıda mən sıfırdan oxşar Spring Security konfiqurasiyası ilə qeyd saxlama xidmətinin necə həyata keçiriləcəyini göstərəcəyəm. Qeyd: Oxucu ən azı Baharla bağlı bir neçə dərslikdən keçsə yaxşıdır, çünki anbarlar, nəzarətçilər və s. haqqında ətraflı izahat verilmədən diqqət yalnız Bahar Təhlükəsizliyinə yönəldiləcək. Əks halda, onsuz da kifayət qədər böyük məqalə çıxacaq. nəhəng olmaq. Məzmun
Məlumat bazasında yeni istifadəçinin peyda olduğunu görürük. Parol şifrələnib.
Qeydlər verilənlər bazasında saxlanılır.
Biz görürük ki, layihə uğurla həyata keçirilir və fəaliyyət göstərir. Tam xoşbəxtlik üçün bizə yalnız sosial şəbəkələr vasitəsilə daxil olmaq imkanı lazımdır. Yaxşı, başlayaq!
İndi həm İstifadəçi, həm də OAuth2Authentication Principal kimi çıxış edə bilər. UserService-də istifadəçinin Google məlumatları vasitəsilə yüklənməsini əvvəlcədən nəzərə aldığımız üçün proqram hər iki istifadəçi növü üçün işləyəcək. Biz layihənin əsas səhifə nəzarətçisini elə dəyişdiririk ki, o, OAuth2-dən istifadə edərək daxil olmuş istifadəçiləri qeydlər səhifəsinə yönləndirsin.
İstifadəçi həm adi formada, həm də Google hesabı vasitəsilə uğurla daxil olur. İstədiyimiz budur! Ümid edirəm ki, bu məqalə veb proqram yaratmaq, onu Spring Security ilə təmin etmək və müxtəlif giriş üsullarını birləşdirməklə bağlı bəzi məqamları aydınlaşdırıb. Tam layihə kodu ilə edə bilərsiniz
- Layihənin yaradılması
- Müəssisələrin yaradılması və Tətbiq Məntiqi
- Klassik Giriş üçün Bahar Təhlükəsizlik Konfiqurasiyası
- Spring Security-də Google-dan istifadə edərək OAuth2-nin qurulması
- Filtr konfiqurasiyası və application.properties
- Tətbiqin Google Bulud Platforması ilə qeydiyyata alınmasının əsas məqamları
- CustomUserInfoTokenServices
- Layihənin yekun açılışı
Layihənin yaradılması
start.spring.io saytına gedirik və layihənin əsasını təşkil edirik:- Veb - daxili Tomcat, url xəritələri və s.-də tətbiqin işə salınması;
- JPA - verilənlər bazası bağlantısı;
- Bığ veb səhifələr yaratmaq üçün istifadə edilən şablon mühərrikidir;
- Təhlükəsizlik - proqram qorunması. Bu məqalə bunun üçün yaradılmışdır.
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.34</version>
</dependency>
application.properties konfiqurasiyası hazırda aşağıdakı kimidir:
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
Müəssisələrin yaradılması və Tətbiq Məntiqi
Müəssisələr
entities
Verilənlər bazası obyektlərini yerləşdirəcəyimiz bir paket yaradaq . İstifadəçi, Spring Security konfiqurasiyası üçün lazım olan User
interfeysi həyata keçirən sinif tərəfindən təsvir ediləcək. UserDetails
İstifadəçinin id, istifadəçi adı (bu e-poçtdur), parol, ad, rol, fəaliyyət bayrağı, Google hesabının adı və e-poçtu ( googleName
və googleUsername
) olacaq.
@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
}
İstifadəçi rolları Spring Security-də girişi tənzimləmək üçün istifadə olunur. Tətbiqimiz yalnız bir roldan istifadə edəcək:
public enum Role implements GrantedAuthority
{
USER;
@Override
public String getAuthority()
{
return name();
}
}
Gəlin id, qeyd başlığı, qeyd gövdəsi və aid olduğu istifadəçinin id-si ilə qeyd sinfi yaradaq:
@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()
}
Anbarlar
Müəssisələri verilənlər bazasında saxlamaq üçün bizim üçün bütün çirkli işləri görəcək depolara ehtiyacımız var. Gəlin bir paket yaradaq , onda biz interfeysdən miras qalmışrepos
interfeysləri yaradacağıq . 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);
}
Nəzarətçilər
Qeydlər xidmətimizdə aşağıdakı səhifələr olacaq:- Ev;
- Qeydiyyat;
- Giriş;
- İstifadəçi qeydlərinin siyahısı.
controllers
sinifdən ibarət paket yaradaq . IndexController
Sinif RegistrationController
istifadəçinin qeydiyyatına cavabdehdir. Post-mapping məlumatı formadan götürür, istifadəçini verilənlər bazasında saxlayır və giriş səhifəsinə yönləndirir. PasswordEncoder
sonra təsvir olunacaq. Parolları şifrələmək üçün istifadə olunur.
@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";
}
Qeydlər siyahısı səhifəsinə cavabdeh olan nəzarətçi hazırda Bahar Təhlükəsizliyi tətbiq edildikdən sonra daha mürəkkəbləşəcək sadələşdirilmiş funksionallığı ehtiva edir.
@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";
}
}
Giriş səhifəsi üçün nəzarətçi yazmayacağıq, çünki o, Spring Security tərəfindən istifadə olunur. Bunun əvəzinə xüsusi konfiqurasiyaya ehtiyacımız olacaq. Həmişə olduğu kimi, gəlin başqa bir paket yaradaq, ona zəng edək config
və sinfi orada yerləşdirək MvcConfig
. Spring Security konfiqurasiyasını yazdığımız zaman "/login" istifadə edərkən hansı səhifəyə istinad etdiyimizi biləcək.
@Configuration
public class MvcConfig implements WebMvcConfigurer
{
public void addViewControllers(ViewControllerRegistry registry)
{
registry.addViewController("/login").setViewName("login");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
}
Səhifələr
Səhifələr yaratmaq üçün Bığ şablon mühərrikindən istifadə edirəm . Başqasını həyata keçirə bilərsiniz, fərq etməz. Bütün səhifələrdə istifadə olunan meta məlumat üçün meta.mustache faylı yaradılmışdır. Layihəmizin səhifələrinin daha gözəl görünməsi üçün Bootstrap da daxildir. Səhifələr "src/main/resources/templates" kataloqunda yaradılır. Fayllarda uzadılmış bığ var. Html kodunu birbaşa məqaləyə yerləşdirmək onu çox böyük edəcək, ona görə də layihənin GitHub repozitoriyasında şablonlar qovluğuna keçid buradadır .Klassik Giriş üçün Bahar Təhlükəsizlik Konfiqurasiyası
Spring Security proqramı və onun resurslarını icazəsiz girişdən qorumağa kömək edir. Paketdə yerləşdirəcəyimizSecurityConfig
miras qalan sinifdə yığcam iş konfiqurasiyası yaradacağıq . Gəlin onu Spring Security dəstəyini aktivləşdirəcək @EnableWebSecurity annotasiyası və bu sinifdə bəzi konfiqurasiyanın olduğunu göstərən @Configuration annotasiyası ilə qeyd edək. Qeyd: avtomatik konfiqurasiya edilmiş pom.xml, Təhlükəsizliyin müəyyən edilmiş şəkildə həyata keçirilməsinə mane olan Spring Boot əsas komponenti 2.1.4.RELEASE versiyasını ehtiva edir. Layihədə münaqişələrin qarşısını almaq üçün versiyanı 2.0.1.RELEASE-ə dəyişmək tövsiyə olunur. WebSecurityConfigurerAdapter
config
Əsas konfiqurasiya SecurityConfig
Konfiqurasiyamız aşağıdakıları edə biləcək:-
Parolları şifrələyin
BCryptPasswordEncoder
:@Autowired private PasswordEncoder passwordEncoder; @Bean PasswordEncoder passwordEncoder() { PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); return passwordEncoder; }
-
Xüsusi yazılmış autentifikasiya provayderindən istifadə edərək daxil olun:
@Autowired private AuthProvider authProvider; @Override protected void configure(AuthenticationManagerBuilder auth) { auth.authenticationProvider(authProvider); }
-
Anonim istifadəçilərə ana səhifə, qeydiyyat və giriş səhifələrinə daxil olmaq imkanı verin. Bütün digər sorğular daxil olmuş istifadəçilər tərəfindən yerinə yetirilməlidir. Daha əvvəl təsvir edilmiş “/login”i giriş səhifəsi kimi təyin edək. Giriş uğurlu olarsa, istifadəçi qeydlərin siyahısı olan səhifəyə aparılacaq, xəta olarsa, istifadəçi giriş səhifəsində qalacaq. Uğurlu çıxışdan sonra istifadəçi əsas səhifəyə aparılacaq.
@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(); }
Fərdi istifadəçi girişi
Öz-özünə yazılmış birAuthProvider
istifadəçiyə yalnız e-poçtla deyil, həm də istifadəçi adı ilə daxil olmağa imkan verəcəkdir.
@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;
}
}
Bildiyiniz kimi, UserService
paketdə yerləşən sinif istifadəçinin yüklənməsinə cavabdehdir services
. Bizim vəziyyətimizdə o, istifadəçini yalnız sahə üzrə deyil username
, daxili tətbiqetmə kimi, həm də istifadəçi adı, Google hesabının adı və Google hesabı e-poçtu ilə axtarır. Son iki üsul OAuth2 vasitəsilə giriş həyata keçirərkən bizim üçün faydalı olacaq. Burada sinif qısaldılmış versiyada verilir.
@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;
}
}
Qeyd: lazımi üsulları -da yazmağı unutmayın UserRepo
!
Nəzarətçini təkmilləşdirək
Spring Security-ni konfiqurasiya etdik. İndi qeyd nəzarətçinizdə bundan yararlanmağın vaxtıdır. İndi hər bir xəritəçəkmə istifadəçini tapmağa çalışacaq əlavə Principal parametrini qəbul edəcək. Niyə mən birbaşa sinfə inyeksiya edə bilmirəmUser
? Sonra sosial şəbəkələr vasitəsilə login yazanda istifadəçi tiplərinin uyğunsuzluğundan münaqişə yaranacaq. Biz əvvəlcədən lazımi çevikliyi təmin edirik. Qeyd nəzarətçi kodumuz indi belə görünür:
@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";
}
Qeyd: layihədə standart olaraq CSRF mühafizəsi aktivləşdirilmişdir , ona görə də ya onu özünüz üçün deaktiv edin (http.csrf().disable()), ya da məqalənin müəllifi kimi csrf işarəsi ilə gizli sahə əlavə etməyi unutmayın. bütün post sorğularına.
Başlayın
Layihəni işə salmağa çalışırıq.Spring Security-də Google-dan istifadə edərək OAuth2-nin qurulması
OAuth2-ni tətbiq edərkən mən Spring-dən bu rəsmi təlimata etibar etdim . OAuth2-ni dəstəkləmək üçün pom.xml-ə aşağıdakı kitabxananı əlavə edin:<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
Gəlin Bahar Təhlükəsizlik konfiqurasiyamızı dəyişdirək SecurityConfig
. Əvvəlcə @EnableOAuth2Client annotasiyasını əlavə edək. Sosial şəbəkələr vasitəsilə daxil olmaq üçün lazım olanları avtomatik olaraq çəkəcək.
Filtr konfiqurasiyası və application.properties
Təhlükəsizlik konfiqurasiyamızda istifadə etmək üçün OAuth2ClientContext inyeksiya edək.@Autowired
private OAuth2ClientContext oAuth2ClientContext;
OAuth2ClientContext istifadəçinin sosial giriş sorğusunu təsdiq edən filtr yaratarkən istifadə olunur. Filtr @EnableOAuth2Client annotasiyası sayəsində əlçatandır. Etməli olduğumuz yeganə şey onu əsas Spring Security filtrindən əvvəl düzgün ardıcıllıqla çağırmaqdır. Yalnız bundan sonra biz OAuth2 ilə giriş prosesi zamanı yönləndirmələri tuta biləcəyik. FilterRegistrationBean
Bunu etmək üçün filtrimizin prioritetini -100 olaraq təyin etdiyimizdən istifadə edirik .
@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;
}
Siz həmçinin konfiqurasiya (HttpSecurity http) funksiyasına yeni filtr əlavə etməlisiniz:
http.addFilterBefore(ssoFilter(), UsernamePasswordAuthenticationFilter.class);
Filtr həmçinin müştərinin Google vasitəsilə qeydiyyatdan keçdiyini bilməlidir. @ConfigurationProperties annotasiyası application.properties-də hansı konfiqurasiya xassələrinin axtarılacağını müəyyən edir.
@Bean
@ConfigurationProperties("google.client")
public AuthorizationCodeResourceDetails google()
{
return new AuthorizationCodeResourceDetails();
}
Doğrulamanı başa çatdırmaq üçün Google istifadəçi məlumatının son nöqtəsini göstərməlisiniz:
@Bean
@ConfigurationProperties("google.resource")
public ResourceServerProperties googleResource()
{
return new ResourceServerProperties();
}
Tətbiqimizi Google Cloud Platform- da qeydiyyatdan keçirdikdən sonra biz application.properties-ə müvafiq prefikslərlə xassələr əlavə edəcəyik:
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
Tətbiqin Google Bulud Platforması ilə qeydiyyata alınmasının əsas məqamları
Yol: API və Xidmətlər -> Etibarnamə OAuth Giriş Sorğusu Pəncərəsi:- Tətbiqin adı: Bahar giriş forması və OAuth2 təlimatı
- Dəstək e-poçt ünvanı: e-poçtunuz
- Google API üçün əhatə dairəsi: e-poçt, profil, openid
- Səlahiyyətli domenlər: me.org
- Tətbiqin əsas səhifəsinə keçid: http://me.org:8080
- Tətbiq məxfilik siyasətinə keçid: http://me.org:8080
- Tətbiqdən istifadə şərtlərinə keçid: http://me.org:8080
- Növ: Veb Tətbiq
- Başlıq: Bahar giriş forması və OAuth2 təlimatı
- İcazə verilən JavaScript mənbələri: http://me.org, http://me.org:8080
- İcazə verilən yönləndirmə URI-ləri: http://me.org:8080/login, http://me.org:8080/login/google
CustomUserInfoTokenServices
Filtr funksiyasının təsvirində Xüsusi sözünə diqqət yetirdinizmi? SinifCustomUserInfoTokenServices
. Bəli, biz blackjack və istifadəçini verilənlər bazasında saxlamaq imkanı ilə öz sinfimizi yaradacağıq! UserInfoTokenServices
IntelliJ IDEA-da Ctrl-N klaviatura qısa yolundan istifadə edərək, standartın necə həyata keçirildiyini tapa və görə bilərsiniz . Gəlin onun kodunu yeni yaradılmış sinfə kopyalayaq CustomUserInfoTokenServices
. Əksəriyyəti dəyişməz qala bilər. Funksiyaların məntiqini dəyişməzdən əvvəl sinifin özəl sahələri kimi əlavə UserRepo
edək PasswordEncoder
. Gəlin onlar üçün tənzimləyicilər yaradaq. SecurityConfig sinfinə @Autowired UserRepo userRepo əlavə edək. Süzgəc yaratma metodunda səhvin göstəricisinin necə yox olmasına baxırıq və sevinirik. Niyə @Autowired birbaşa CustomUserInfoTokenServices-ə tətbiq oluna bilmədi? Çünki bu sinif asılılığı qəbul etməyəcək, çünki onun özü heç bir Spring annotasiyası ilə qeyd olunmur və onun konstruktoru filtr elan edildikdə açıq şəkildə yaradılır. Müvafiq olaraq, Baharın DI mexanizmi bu barədə bilmir. Bu sinifdə hər hansı bir şeyə @Autowired annotasiyası qoysaq, istifadə edildikdə NullPointerException alacağıq. Ancaq açıq təyinedicilər vasitəsilə hər şey çox yaxşı işləyir. Lazımi komponentləri həyata keçirdikdən sonra maraq doğuran əsas obyekt istifadəçi haqqında məlumatı olan Map<String, Object>-in götürüldüyü loadAuthentication funksiyasına çevrilir. Məhz bu layihədə mən sosial şəbəkə vasitəsilə daxil olmuş istifadəçinin məlumat bazasında saxlanmasını həyata keçirdim. OAuth2 provayderi kimi Google hesabından istifadə etdiyimiz üçün xəritədə Google üçün xarakterik olan “alt” sahənin olub-olmadığını yoxlayırıq. Əgər varsa, bu o deməkdir ki, istifadəçi haqqında məlumat düzgün qəbul edilib. Yeni istifadəçi yaradırıq və onu verilənlər bazasında saxlayırıq.
@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);
}
Bir neçə provayderdən istifadə edərkən, siz bir CustomUserInfoTokenServices-də müxtəlif seçimləri təyin edə və filtr bəyannamə metodunda oxşar xidmətlərin müxtəlif siniflərini qeyd edə bilərsiniz.
@GetMapping("/")
public String index(Principal principal)
{
if(principal != null)
{
return "redirect:/notes";
}
return "index";
}
GO TO FULL VERSION