Baru-baru ini, saya mengalami masalah dalam proyek: front-end dan back-end dipisahkan, front-end dilakukan dengan vue, semua permintaan data menggunakan sumber daya vue, dan tidak ada formulir yang digunakan, sehingga interaksi data digunakan oleh JSON, latar belakang menggunakan boot musim semi, dan verifikasi izin menggunakan keamanan musim semi. Karena keamanan musim semi digunakan sebelumnya, mereka memproses halaman, dan kali ini mereka hanya memproses permintaan AJAX, sehingga mereka mencatat beberapa masalah yang mereka temui. Solusi di sini tidak hanya cocok untuk permintaan AJAX, tetapi juga menyelesaikan verifikasi permintaan seluler.
Buat proyek
Pertama -tama, kita perlu membuat proyek boot musim semi. Saat membuatnya, kita perlu memperkenalkan Web, Spring Security, MySQL dan MyBatis (kerangka basis data sebenarnya sewenang -wenang, saya menggunakan mybatis di sini). Setelah pembuatan, file ketergantungan adalah sebagai berikut:
<dependency> <GroupId> org.mybatis.spring.boot </groupid> <ArTifactId> mybatis-spring-boot-starter </t Artifactid> <version> 1.3.1 </version> </dependency> <grouptid> org.springframework.boot </groupid> <grouplency> <groupid> <ArTifactId> Spring-Boot-Starter-Security </stifactid> </dependency> <dependency> <GroupId> org.springframework.boot </groupid> <ArtiFacTID> </ArtifactId> <ArtifactId> <groupid> Mysql </Artifactid> </Dependency> <dependency> <groupid> Mysql </Groupl </Groupl> <ArtifactId> mysql-connector-java </stifactid> <scope> runtime </opope> </dependency> <dependency> <groupid> commons-codec </groupid> <ArtifactId> commons-codec </artifactid> <versi> 1.11 </version> </Dependency>
Perhatikan bahwa ketergantungan Commons-Codec terakhir ditambahkan secara manual oleh saya. Ini adalah proyek open source dari Apache yang dapat digunakan untuk menghasilkan pencernaan pesan MD5. Saya hanya akan memproses kata sandi dalam teks berikut.
Buat database dan konfigurasikan
Untuk menyederhanakan logika, saya telah membuat tiga tabel di sini, yaitu tabel pengguna, tabel peran, dan tabel asosiasi peran pengguna, sebagai berikut:
Selanjutnya, kita perlu membuat konfigurasi sederhana dari database kita di application.properties. Di sini kami ditentukan oleh situasi spesifik Anda.
spring.datasource.url = jdbc: mysql: ///vueblogspring.datasource.username=rootspring.datasource.password=123
Konstruksi kelas entitas
Di sini terutama mengacu pada membangun kelas pengguna. Kelas pengguna di sini cukup istimewa dan harus mengimplementasikan antarmuka pengguna, sebagai berikut:
Pengguna kelas publik mengimplementasikan UserDetails {Private Long ID; nama pengguna string pribadi; kata sandi string pribadi; nama panggilan string pribadi; diaktifkan boolean pribadi; Daftar Pribadi <lance> Peran; @Override public boolean isaccountnonexpired () {return true; } @Override public boolean isAccountNonLocked () {return true; } @Override public boolean isCredentialSnonexpired () {return true; } @Override public boolean isenabled () {return diaktifkan; } @Override Public List <BanterAthority> getAuthority () {List <BanteriTauthority> otoritas = new ArrayList <> (); untuk (peran peran: peran) {otoritas.add (new SimpleGrantEdAuthority ("Role_" + Role.getName ())); } mengembalikan otoritas; } // Getter/Setter Omit ...} Setelah mengimplementasikan antarmuka UserDetails, ada beberapa metode dalam antarmuka ini yang perlu kita terapkan. Keempat metode yang mengembalikan Boolean semuanya diketahui dan diketahui. Diaktifkan menunjukkan apakah akun periode diaktifkan. Bidang ini memang ada di database saya. Oleh karena itu, menurut hasil kueri, yang lain kembali secara langsung untuk periode sederhana. Metode GetAuthority mengembalikan informasi peran pengguna saat ini. Peran pengguna sebenarnya adalah data dalam peran. Data dalam peran dikonversi ke daftar <BanteriTauthority> dan kemudian dikembalikan. Ada titik yang perlu diperhatikan di sini. Karena nama peran yang saya simpan dalam database semuanya seperti 'Super Administrator', 'pengguna biasa', dll., Dan jangan mulai dengan karakter seperti ROLE_ , jadi Anda perlu menambahkan ROLE_ secara manual di sini, ingat.
Ada juga kelas entitas peran, yang relatif sederhana dan dapat dibuat sesuai dengan bidang database. Saya tidak akan mengulanginya di sini.
Buat UsersEnVice
UserService di sini juga cukup istimewa, dan perlu mengimplementasikan antarmuka UserDetailsService, sebagai berikut:
@ServicePublic kelas UsersEver mengimplementasikan userDetailsService {@Autowired usermapper userMapper; @Autowired RolesMapper RolesMapper; @Override Public UserDetails LoadUserByUserName (String S) melempar UserNamenotFoundException {user user = userMapper.LoadUserByUserName (s); if (user == null) {// hindari mengembalikan null, di sini mengembalikan objek pengguna yang tidak berisi nilai apa pun, dan verifikasi juga akan gagal dalam proses perbandingan kata sandi yang lebih baru mengembalikan pengguna baru (); } // Permintaan informasi peran pengguna dan kembali ke pengguna. Daftar <lance> peran = rolesMapper.getRolesByUid (user.getId ()); user.setrole (peran); Pengguna Kembali; }}Setelah mengimplementasikan antarmuka UserDetailsService, kita perlu mengimplementasikan metode LoadUserbyUserName di antarmuka, yaitu permintaan pengguna berdasarkan nama pengguna. Dua pemetaan di mybatis disuntikkan di sini, Usermapper digunakan untuk meminta pengguna, dan peran Rolesmapper digunakan untuk meminta peran. Dalam metode LoadUserByUserName, permintaan pertama pengguna sesuai dengan parameter yang dilewati (parameter adalah nama pengguna yang dimasukkan ketika pengguna masuk). Jika pengguna yang ditemukan adalah NULL, Anda dapat secara langsung melempar pengecualian UserNamenotFoundException. Namun, untuk kenyamanan pemrosesan, saya mengembalikan objek pengguna tanpa nilai apa pun. Dengan cara ini, selama proses perbandingan kata sandi berikutnya, Anda akan menemukan bahwa login gagal (di sini Anda dapat menyesuaikannya sesuai dengan kebutuhan bisnis Anda). Jika pengguna yang ditemukan bukan nol, kami akan meminta peran pengguna berdasarkan ID pengguna dan memasukkan hasil kueri ke objek pengguna. Hasil kueri ini akan digunakan dalam metode GetAuthority dari objek pengguna.
Konfigurasi Keamanan
Mari kita lihat konfigurasi keamanan saya terlebih dahulu, dan kemudian saya akan menjelaskannya satu per satu:
@ConfigurationPublic kelas WebSecurityConfig Extends WebSecurityConfigurerAdapter {@Autowired UserService UsersEverService; @Override dilindungi void configure (authenticationManagerBuilder auth) melempar Exception {auth.userdetailsService (Userservice) .passwordEncoder (kata sandi baru () {@override public encode (charsequence). * @param Charsequence Plaintext * @param s Ciphertext * @Return */ @Override Public Boolean Matches (Charsequence Charsequence, String S) {return S.Equals (digestutils.md5digestAshex (charsequence.tostring (). getBytes ())); } @Override Protected void configure (httpsecurity http) melempar Exception {http.authorizeRequests () .AntMatchers ("/admin/**"). Hasrole ("Super Admin") .Anyrequest (). Autentikasi () in.and (). FormLogin (). LoginPage ("/LOGIN_PAGE"). SuccessHandler (Authentications NEW ServletException {httpservletResponse.setContentType ("Application/JSON; Charset = UTF-8"); }}) .failureHandler (new AuthenticationFailureHandler () {@Override public void onAuthenticationFailure (httpservletRequest httpservletRequest, httpservletResception {httpservletsception {loughlexception {httpserponsception {throwsception {httpsception {throwsception {httpsception {httpsception {httpsception {httpsception {httpsception e) httpservletResponse.setContentType ("Application/JSON; Charset = UTF-8"); }). LoginProcessingUrl ("/Login") .usernameparameter ("nama pengguna"). Kata SandiParameter ("Kata Sandi"). Permitall () .and (). Logout (). ijinall (). Dan (). CSRF (). Disable (); } @Override public void configure (WebSecurity Web) melempar Exception {Web.ignoring (). Antmatchers ("/Reg"); }}Ini adalah inti dari konfigurasi kami. Tolong dengarkan saya:
1. Pertama -tama, ini adalah kelas konfigurasi, jadi ingatlah untuk menambahkan anotasi @configuration. Karena ini adalah konfigurasi keamanan musim semi, Anda harus mewarisi WebSecurityConfigurerAdapter.
2. Suntikkan UserService yang baru saja dibuat dan kami akan menggunakannya nanti.
3. Metode konfigurasi (authenticationManagerBuilder auth) digunakan untuk mengonfigurasi metode otentikasi kami, dan lulus layanan pengguna dalam metode auth.userdetailsService (), sehingga metode yang diseduh oleh Pengguna dan Pengguna. Kata sandi Plaintext juga diperlukan untuk memprosesnya saat masuk, jadi saya menambahkan kata sandi, dan setelah menambahkan kata sandi, saya dapat secara langsung baru kelas internal anonim dari kata sandi. Ada dua metode untuk diimplementasikan di sini, dan Anda dapat mengetahui arti metode dengan melihat namanya. Metode pertama Encode jelas mengenkripsi plaintext. Di sini saya menggunakan MD5 Message Digest. Metode implementasi spesifik disediakan oleh ketergantungan commons-codec; Metode kedua yang cocok adalah perbandingan kata sandi, dua parameter, parameter pertama adalah kata sandi plaintext, dan yang kedua adalah ciphertext. Di sini Anda hanya perlu mengenkripsi plaintext dan membandingkannya dengan ciphertext (jika Anda tertarik dengan ini, Anda dapat terus mempertimbangkan untuk menambahkan garam ke kata sandi).
4. Configure (httpsecurity http) digunakan untuk mengonfigurasi aturan otentikasi kami, dll. Metode otorizeRequests berarti bahwa konfigurasi aturan otentikasi diaktifkan, antmatcher ("/admin/**"). Hasrole ("Super Administrator") berarti bahwa Path of /admin/** perlu diakses oleh pengguna dengan itu '. Saya melihat di internet bahwa teman -teman saya memiliki pertanyaan tentang apakah akan menambahkan ROLE_ dalam metode hasrole. Jangan menambahkannya di sini. Jika Anda menggunakan metode HasAuthority, Anda perlu menambahkannya. AnyRequest (). Otentikasi () berarti bahwa semua jalur lain perlu diautentikasi/masuk sebelum diakses. Selanjutnya kami mengonfigurasi halaman login sebagai login_page, jalur pemrosesan login adalah /login, nama pengguna login adalah nama pengguna, dan kata sandi adalah kata sandi. Kami mengkonfigurasi jalur ini untuk mengakses secara langsung, dan keluar dari login juga dapat diakses secara langsung, dan akhirnya menutup CSRF. Di Successhandler, gunakan respons untuk mengembalikan JSON yang telah berhasil masuk. Ingat untuk tidak menggunakan defaultSuccessurl. DefaultSuccessurl adalah halaman yang dialihkan hanya setelah masuk dengan sukses. Alasan yang sama juga digunakan untuk Failhandler.
5. Saya telah mengkonfigurasi beberapa aturan penyaringan dalam metode konfigurasi (WebSecurity Web), jadi saya tidak akan masuk ke detail.
6. Selain itu, untuk file statis, seperti /images/** , /css/** , dan /js/** , standarnya tidak dicegat.
Pengontrol
Akhirnya, mari kita lihat pengontrol kita, sebagai berikut:
@RestControllerPublic Class LoginRegController {/** * Jika Anda secara otomatis melompat ke halaman ini, itu berarti bahwa pengguna tidak masuk, dan Anda dapat mengembalikan prompt yang sesuai * <p> * Jika Anda ingin mendukung Formulir, Anda dapat menilai jenis permintaan dalam metode ini, dan kemudian memutuskan apakah akan kembali ke JSON atau HTML Page * * @ Respbean loginpage () {return new respbean ("error", "belum masuk, silakan masuk!"); }} Secara keseluruhan, pengontrol ini relatif sederhana. Respbean mengembalikan JSON sederhana, yang tidak terperinci. Yang perlu Anda perhatikan di sini adalah login_page . Halaman login yang kami konfigurasi adalah login_page . Namun pada kenyataannya, login_page bukan halaman, tetapi json. Ini karena ketika saya mengunjungi halaman lain tanpa masuk, keamanan musim semi akan secara otomatis melompat ke halaman login_page . Namun, dalam permintaan AJAX, lompatan semacam ini tidak diperlukan. Yang saya inginkan hanyalah prompt untuk masuk atau tidak, jadi saya dapat mengembalikan JSON di sini.
tes
Akhirnya, teman dapat menggunakan alat seperti tukang pos atau restclient untuk menguji masalah login dan izin, jadi saya tidak akan menunjukkannya.
OK, setelah pengantar di atas, saya percaya bahwa teman -teman telah memahami Boot Spring+Spring Security menangani permintaan login AJAX. Oke, itu saja untuk artikel ini. Jika Anda memiliki pertanyaan, silakan tinggalkan pesan untuk didiskusikan.