Arzamy ýazanymda, ulanyjynyň e-poçta we sosial ulgamlar arkaly nädip hasaba alynmalydygy barada anyk makalalaryň ýoklugyna duş geldim. Klassiki giriş formasyny düzmek boýunça gowy sapaklar bardy. OAuth2 -de gowy sapaklar bardy . Iki usuly nädip birleşdirmelidigi barada jenaýat taýdan az maglumat bardy. Gözleg prosesinde, amatly çözgüt tapyp bildik. Iň soňky hakykatdygyny öňe sürmeýär, ýöne öz wezipesini ýerine ýetirýär. Bu makalada şuňa meňzeş Bahar Howpsuzlygy konfigurasiýasy bilen bellik saklaýyş hyzmatyny noldan nädip durmuşa geçirmelidigini görkezerin. Bellik: Okyjy Bahar hakda azyndan iki sapakdan geçen bolsa gowy, sebäbi ammarlar, gözegçilik edijiler we ş.m. barada jikme-jik düşündiriş bermezden, diňe Bahar Howpsuzlygyna gönükdiriler, ýogsam, eýýäm uly bir makala çykar. ullakan bol. Mazmuny
Maglumat bazasynda täze ulanyjynyň peýda bolandygyny görýäris. Parol şifrlenen
Bellikler maglumatlar bazasyna ýazylýar.
Taslamanyň üstünlikli başlandygyny we işleýändigini görýäris. Doly bagt üçin diňe sosial ulgamlar arkaly girmek ukybymyz gerek. Geliň, başlalyň!
Indi Ulanyjy we OAuth2Autifikasiýa hem prinsipial bolup biler. “UserService” -da ulanyjynyň Google maglumatlary arkaly ýüklenmegini öňünden göz öňünde tutanymyzdan soň, programma iki ulanyjy üçinem işlär. Taslamanyň esasy sahypa gözegçisini, OAuth2 ulanyp giren ulanyjylary bellikler sahypasyna ugrukdyrar ýaly üýtgedýäris.
Ulanyjy adaty görnüşde we Google hasaby arkaly üstünlikli girýär. Bu biziň isleýän zadymyz! Bu makala web programmasyny döretmek, Bahar howpsuzlygy bilen üpjün etmek we dürli giriş usullaryny birleşdirmek baradaky käbir nokatlary aýyrdy diýip umyt edýärin. Taslamanyň doly kody bilen edip bilersiňiz
- Taslama döretmek
- Edaralary döretmek we amaly logika
- Klassiki giriş üçin bahar howpsuzlygyny sazlamak
- “Bahar Howpsuzlygy” -da mysal hökmünde Google ulanyp, OAuth2 gurmak
- Filtr konfigurasiýasy we programma.properties
- “Google Cloud Platform” -da programmany hasaba almagyň esasy pursatlary
- CustomUserInfoTokenServices
- Taslamanyň ahyrky başlangyjy
Taslama döretmek
Start.spring.io- a girýäris we taslamanyň esasyny düzýäris:- Web - gurlan Tomcat, url kartalary we şuňa meňzeşler üçin programma açmak;
- JPA - maglumat bazasyna birikmek;
- Murt, web sahypalaryny döretmek üçin ulanylýan şablon hereketlendirijisidir;
- Howpsuzlyk - amaly goramak. Bu makala üçin döredilen zat.
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.34</version>
</dependency>
Programma.properties konfigurasiýasy häzirki wagtda aşakdaky ýaly:
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
Edaralary döretmek we amaly logika
Kärhanalar
entities
Maglumat bazasynyň subýektlerini ýerleşdirjek bukjany döredeliň . Ulanyjy , Bahar Howpsuzlygy konfigurasiýasy üçin zerur boljak User
interfeýsi ýerine ýetirýän synp bilen düşündiriler . UserDetails
Ulanyjynyň şahsyýeti, ulanyjy ady (bu e-poçta), parol, ady, roly, işjeňlik baýdagy, Google hasabynyň ady we e-poçta ( googleName
we googleUsername
) bolar.
@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
}
Ulanyjy rollary Bahar Howpsuzlygyna girişi sazlamak üçin ulanylýar. Arzamyz diňe bir roly ulanar:
public enum Role implements GrantedAuthority
{
USER;
@Override
public String getAuthority()
{
return name();
}
}
Geliň, ID, bellik ady, bellik göwrümi we ulanyjynyň şahsyýet belgisi bilen bellik synpyny döredeliň:
@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()
}
Ammarlar
Edaralary maglumat bazasyna ýazmak üçin bize ähli hapa işleri etjek ammarlar gerek. Geliň, bukja döredeliň , onda interfeýsden miras galanrepos
interfeýsler dörederis . 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);
}
Dolandyryjylar
Bellikler hyzmatymyzda aşakdaky sahypalar bolar:- Öý;
- Registrasiýa;
- Giriş
- Ulanyjy bellikleriniň sanawy.
controllers
synp döredeliň. IndexController
Synp RegistrationController
ulanyjyny hasaba almak üçin jogapkärdir. Kartadan soňky karta formadan maglumatlary alýar, ulanyjyny maglumat bazasyna ýazdyrýar we giriş sahypasyna gönükdirýär. PasswordEncoder
soň düşündiriler. Parollary şifrlemek üçin ulanylýar.
@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";
}
Bellikler sanawy sahypasyna jogapkär gözegçilik ediji häzirki wagtda Bahar Howpsuzlygy ýerine ýetirilenden soň has çylşyrymlaşjak ýönekeýleşdirilen funksiýany öz içine alýar.
@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ş sahypasy üçin gözegçilik ediji ýazmarys, sebäbi Bahar Howpsuzlygynda ulanylýar. Munuň ýerine ýörite konfigurasiýa gerek bolar. Hemişe bolşy ýaly, başga bir paket döredeliň, oňa jaň edeliň config
we synpy şol ýere ýerleşdireliň MvcConfig
. Bahar howpsuzlygy konfigurasiýasyny ýazanymyzda, "/ login" ulananymyzda haýsy sahypa ýüz tutýandygymyzy biler.
@Configuration
public class MvcConfig implements WebMvcConfigurer
{
public void addViewControllers(ViewControllerRegistry registry)
{
registry.addViewController("/login").setViewName("login");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
}
Sahypalar
Sahypa döretmek üçin Mustache şablon motoryny ulanýaryn . Başga birini durmuşa geçirip bilersiňiz, tapawudy ýok. Athli sahypalarda ulanylýan meta maglumatlary üçin meta.mustache faýly döredildi. Şeýle hem taslamamyzyň sahypalaryny has owadan etmek üçin “Bootstrap” -y öz içine alýar. Sahypalar "src / main / resources / templates" katalogynda döredilýär. Faýllarda giňeltme murtlary bar. Html koduny gönüden-göni makalada ýerleşdirmek ony gaty uly eder, şonuň üçin taslamanyň GitHub ammaryndaky şablonlar bukjasyna baglanyşyk bar .Klassiki giriş üçin bahar howpsuzlygyny sazlamak
Bahar howpsuzlygy, programmany we çeşmelerini rugsatsyz girişden goramaga kömek edýär. Bukjada ýerleşdirjekSecurityConfig
miras galan synpymyzda gysga iş konfigurasiýasyny dörederis . Geliň, Bahar Howpsuzlyk goldawyny üpjün etjek @EnableWebSecurity düşündirişi we bu synpda käbir konfigurasiýanyň bardygyny görkezýän @ Konfigurasiýa belligi bilen belläliň. Bellik: awtomatiki düzülen pom.xml-de Howpsuzlygyň bellenilen tertipde durmuşa geçirilmeginiň öňüni alýan “Spring Boot” ene komponentiniň 2.1.4.RELEASE wersiýasy bar. Taslamadaky gapma-garşylyklaryň öňüni almak üçin wersiýasyny 2.0.1.RELEASE üýtgetmek maslahat berilýär. WebSecurityConfigurerAdapter
config
Esasy konfigurasiýa SecurityConfig
Biziň konfigurasiýamyz başarar:-
Paroly şifrlemek
BCryptPasswordEncoder
:@Autowired private PasswordEncoder passwordEncoder; @Bean PasswordEncoder passwordEncoder() { PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); return passwordEncoder; }
-
Writtenörite ýazylan tanamaklyk üpjün edijisini ulanyp giriň:
@Autowired private AuthProvider authProvider; @Override protected void configure(AuthenticationManagerBuilder auth) { auth.authenticationProvider(authProvider); }
-
Anonim ulanyjylara baş sahypa, hasaba alyş we giriş sahypalaryna girmäge rugsat beriň. Beýleki haýyşlaryň hemmesi hasaba giren ulanyjylar tarapyndan ýerine ýetirilmelidir. Öň beýan edilen “/ login” -i giriş sahypasy hökmünde belläliň. Giriş üstünlikli bolsa, ulanyjy bellikleriň sanawy bolan sahypa çykarylar; ýalňyşlyk bar bolsa, ulanyjy giriş sahypasynda galar. Üstünlikli çykyşdan soň ulanyjy esasy sahypa çykarylar.
@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(); }
Ulanyjy girişi
Özbaşdak ýazylan ulanyjy,AuthProvider
diňe bir e-poçta bilen däl, eýsem ulanyjy ady bilen hem girmäge mümkinçilik berer.
@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;
}
}
Üns beren bolsaňyz, UserService
bukjada ýerleşýän synp ulanyjyny ýüklemek üçin jogapkärdir services
. Biziň ýagdaýymyzda, ulanyjyny diňe username
içerki ýerine ýetiriş ýaly meýdan boýunça däl, eýsem ulanyjy ady, Google hasaby ady we Google hasaby e-poçta bilen hem gözleýär. Soňky iki usul, OAuth2 arkaly giriş amala aşyrylanda bize peýdaly bolar. Bu ýerde synp gysgaldylan görnüşde berilýär.
@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;
}
}
Bellik: zerur usullary ýazmagy ýatdan çykarmaň UserRepo
!
Dolandyryjyny kämilleşdireliň
Bahar howpsuzlygyny düzdük. Indi bellikler dolandyryjyňyzdan peýdalanmagyň wagty geldi. Indi her kartalaşdyrmak, ulanyjyny tapmaga synanyşjak goşmaça Esasy parametr kabul eder. Näme üçin klasa göni sanjym edip bilemokUser
? Soňra sosial ulgamlar arkaly giriş ýazanymyzda ulanyjy görnüşleriniň gabat gelmezligi sebäpli gapma-garşylyk ýüze çykar. Zerur çeýeligi öňünden berýäris. Belliklerimizi dolandyryjy kody indi şeýle 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";
}
Bellik: taslama deslapky görnüşde CSRF goragy bar , şonuň üçin ony özüňiz öçüriň (http.csrf (). Disable ()), ýa-da makalanyň awtory hökmünde csrf belligi bilen gizlin meýdan goşmagy ýatdan çykarmaň ähli poçta isleglerine.
Işletmek
Taslamany durmuşa geçirmäge synanyşýarys.“Bahar Howpsuzlygy” -da mysal hökmünde Google ulanyp, OAuth2 gurmak
OAuth2 durmuşa geçirilende, Bahardan bu resmi sapaga bil bagladym . OAuth2-ni goldamak üçin pom.xml-e aşakdaky kitaphanany goşuň:<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
Geliň, Bahar Howpsuzlyk konfigurasiýamyzy üýtgedeliň SecurityConfig
. Ilki bilen, @ EnableOAuth2Client düşündirişini goşalyň. Sosial ulgamlar arkaly girmeli zatlaryňyzy awtomatiki usulda çeker.
Filtr konfigurasiýasy we programma.properties
Howpsuzlyk konfigurasiýamyzda ulanmak üçin OAuth2ClientContext sanjym edeliň.@Autowired
private OAuth2ClientContext oAuth2ClientContext;
OAuth2ClientContext ulanyjynyň sosial giriş islegini tassyklaýan süzgüç döredilende ulanylýar. Süzgüç @ EnableOAuth2Client düşündirişiniň kömegi bilen elýeterlidir. Biziň etmeli zadymyz, Baharyň esasy süzgüçinden öň ony dogry tertipde çagyrmak. Diňe şondan soň OAuth2 bilen giriş prosesinde gönükdirmeleri alyp bileris. Munuň üçin FilterRegistrationBean
süzgüçimiziň ileri tutulýan ýerini -100-e çenli ulanýarys.
@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;
}
Şeýle hem, konfigurasiýa (HttpSecurity http) funksiýasyna täze süzgüç goşmaly:
http.addFilterBefore(ssoFilter(), UsernamePasswordAuthenticationFilter.class);
Süzgüç, müşderiniň Google arkaly hasaba alnandygyny hem bilmelidir. @ConfigurationProperties düşündiriş, haýsy konfigurasiýa aýratynlyklaryny application.properties-de gözlemelidigini kesgitleýär.
@Bean
@ConfigurationProperties("google.client")
public AuthorizationCodeResourceDetails google()
{
return new AuthorizationCodeResourceDetails();
}
Hakyky tassyklamany tamamlamak üçin Google ulanyjynyň maglumat nokadyny görkezmeli:
@Bean
@ConfigurationProperties("google.resource")
public ResourceServerProperties googleResource()
{
return new ResourceServerProperties();
}
“Google Cloud Platform” -da programmamyzy hasaba alanymyzdan soň , “application.properties” -e degişli prefiksler bilen häsiýetler goşarys:
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” -da programmany hasaba almagyň esasy pursatlary
: Ol: API we hyzmatlar -> Şahsyýetnamalar OAuth giriş talap penjiresi:- Programmanyň ady: Baharyň giriş formasy we OAuth2 gollanmasy
- Goldaw e-poçta salgysy: e-poçtaňyz
- Google API-iň çäkleri: e-poçta, profil, açyk
- Ygtyýarly domenler: me.org
- Programmanyň esasy sahypasyna baglanyşyk: http://me.org:8080
- Programmanyň gizlinlik ýörelgesine baglanyşyk: http://me.org:8080
- Ulanyş şertlerine baglanyşyk: http://me.org:8080
- Görnüşi: Web goýmasy
- Ady: Baharyň giriş formasy we OAuth2 gollanmasy
- Rugsat berlen JavaScript çeşmeleri: http://me.org, http://me.org:8080
- URI-leri gönükdirmäge rugsat berilýär: http://me.org:8080/login, http://me.org:8080/login/google
CustomUserInfoTokenServices
Süzgüç funksiýasynyň beýanynda Custom sözüne üns berdiňizmi? SynpCustomUserInfoTokenServices
. Hawa, blackjack we ulanyjyny maglumat bazasynda saklamak ukyby bilen öz synpymyzy dörederis! UserInfoTokenServices
IntelliJ IDEA-da Ctrl-N klawiatura salgylanmasyny ulanyp, deslapky ýerine ýetirilişini tapyp we görüp bilersiňiz . Kody täze döredilen klasa göçüreliň CustomUserInfoTokenServices
. Köpüsi üýtgewsiz galyp biler. Funksiýalaryň logikasyny üýtgetmezden ozal, synpyň şahsy meýdanlary hökmünde UserRepo
goşalyň PasswordEncoder
. Geliň, olar üçin sazlaýjylar döredeliň. “SecurityConfig” synpyna @Autowired UserRepo userRepo goşalyň. Süzgüç döretmek usulyndaky ýalňyşlygyň görkezijisiniň nädip ýitip barýandygyna seredýäris we begenýäris. Näme üçin @Autowired-i göni CustomUserInfoTokenServices ulanyp bolmaýar? Sebäbi bu synp garaşlylygy saýlamaz, sebäbi özi hiç hili Bahar ýazgysy bilen bellik edilmeýär we süzgüç yglan edilende konstruktory aç-açan döredilýär. Şoňa laýyklykda Baharyň DI mehanizmi bu barada bilmeýär. @Autowired-i bu synpdaky haýsydyr bir zada düşündirsek, ulanylanda NullPointerException alarys. Emma aç-açan sazlaýjylaryň üsti bilen hemme zat gaty gowy işleýär. Zerur komponentleri ýerine ýetirenimizden soň, esasy gyzyklanma obýekti ulanyjy barada maglumatly Karta <Setir, Obýekt> alynýan “loadAutifikasiýa” funksiýasyna öwrülýär. Hut şu taslamada sosial ulgamdan giren ulanyjynyň maglumat bazasyna tygşytlamagyny amala aşyrdym. Google hasabyny OAuth2 üpjün ediji hökmünde ulanýandygymyz sebäpli, kartada Google üçin adaty “kiçi” meýdançanyň bardygyny ýa-da ýokdugyny barlaýarys. Eger bar bolsa, ulanyjy baradaky maglumatlaryň dogry alnandygyny aňladýar. Täze ulanyjy döredýäris we maglumat bazasyna ýazdyrýarys.
@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);
}
Birnäçe üpjün edijini ulananyňyzda, bir CustomUserInfoTokenServices-de dürli wariantlary kesgitläp bilersiňiz we süzgüç deklarasiýa usulynda şuňa meňzeş hyzmatlaryň dürli synplaryny bellige alyp bilersiňiz.
@GetMapping("/")
public String index(Principal principal)
{
if(principal != null)
{
return "redirect:/notes";
}
return "index";
}
GO TO FULL VERSION