Ringkasan
Seperti yang kita semua tahu, menggunakan JWT untuk verifikasi izin. Dibandingkan dengan sesi, keuntungan sesi adalah bahwa sesi membutuhkan sejumlah besar memori server, dan ketika banyak server digunakan, itu akan melibatkan masalah sesi bersama, yang lebih merepotkan ketika mengakses terminal seluler seperti ponsel.
JWT tidak perlu disimpan di server dan tidak menempati sumber daya server (yaitu, stateless). Setelah pengguna masuk, ia melekat pada token saat mengakses permintaan yang memerlukan izin (biasanya ditetapkan di header permintaan HTTP). JWT tidak memiliki masalah berbagi beberapa server, juga tidak memiliki masalah akses seluler di ponsel. Jika untuk meningkatkan keamanan, token dapat terikat pada alamat IP pengguna
Proses front-end
Pengguna masuk melalui Ajax untuk mendapatkan token
Setelah itu, saat mengakses memerlukan izin, pasang token untuk mengakses.
<! Doctype html> <html lang = "en"> <head> <meta charset = "utf-8"> <itement> title </iteme> <script src = "http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.min.js"> </script> "" "" "" Fungsi login () {$ .post ("http: // localhost: 8080/auth/login", {username: $ ("#username"). val (), kata sandi: $ ("#password"). Val ()}, fungsi (data) {console.log (data); header = data; {{{{data) {data) {data). "Get", URL: "http: // localhost: 8080/userpage", beforeSend: function (request) {request.setRequestheader ("otorisasi", header); } </script> </head> <body> <fieldset> <Gegend> Harap masuk </ Legend> <label> nama pengguna </label> <input type = "text" id = "username"> <label> Kata sandi </label> <input type = "text" ID = "kata sandi"> <input type = "tombol" onclick = "LOGIN ()" Nilai "=" FOR "> <input type =" onklick = "LoPer =" LOGIN "=" FOR "> ID = "TOUSERPAGEBTN" ONCLick = "TOUSERPAGEBTN ()"> ACCESS USERPAGE </aton> </body> </html>Proses Backend (Spring Boot + Spring Security + JJWT)
Ide:
Tulis kelas entitas pengguna dan masukkan sepotong data
Kelas Entitas Pengguna (Pengguna)
@Data @EntityPublic kelas pengguna {@id @generatedValue private int id; nama string pribadi; kata sandi string pribadi; @ManyToMany(cascade = {CascadeType.REFRESH}, fetch = FetchType.EAGER) @JoinTable(name = "user_role", joinColumns = {@JoinColumn(name = "uid", referencedColumnName = "id")}, inverseJoinColumns = {@JoinColumn(name = "rid", referencedColumnName = "ID")}) Daftar Privat <lance> Peran;} Kelas Entitas Peran (Izin)
@Data @EntityPublic Class Role {@ID @DeneratedValue Private int ID; nama string pribadi; @Manytomany (mappedby = "peran") daftar pribadi <user> pengguna;} Masukkan data
Tabel pengguna
| pengenal | nama | kata sandi |
|---|---|---|
| 1 | Linyuan | 123 |
Tabel peran
| pengenal | nama |
|---|---|
| 1 | PENGGUNA |
Tabel User_Role
| uid | menyingkirkan |
|---|---|
| 1 | 1 |
Antarmuka lapisan DAO, memperoleh data melalui nama pengguna, dan mengembalikan objek opsional dengan nilai java8
Antarmuka publik USErrepository memperluas repositori <user, integer> {opsional <user> findByname (nama string);} Tulis Logindto untuk transfer data dengan ujung depan
@DataPublic CLASS LOGINDTO mengimplementasikan Serializable {@notblank (message = "Nama pengguna tidak bisa kosong") Private String username; @Notblank (pesan = "Kata sandi tidak bisa kosong") kata sandi string pribadi;} Tulis alat pembuatan token dan buatlah menggunakan perpustakaan JJWT. Ada tiga metode secara total: menghasilkan token (kembalikan string), parse token (kembalikan objek otentikasi otentikasi), dan verifikasi token (kembalikan nilai boolean)
@ComponentPublic kelas jwttokenutils {private final logger log = loggerFactory.getLogger (jwttokenutils.class); private static final string otoritas_key = "auth"; Private String Secretkey; // menandatangani kunci pribadi tokenValidityInmilliseconds; // tanggal kedaluwarsa swasta lama validitasinmillisecondsforrememberme; // (ingat saya) tanggal kedaluwarsa @postconstruct public void init () {this.secretkey = "linyuanmima"; int decondin1day = 1000 * 60 * 60 * 24; this.tokenValidityInmilliseconds = detik 1day * 2l; this.tokenValidityInmillisecondsforrememberme = detik 1day * 7l; } private final static long expirationTime = 432_000_000; // Buat token public string createToken (otentikasi otentikasi, boolean ingatme) {string otoritas = authentication.getauthority (). Stream () // dapatkan string izin pengguna, seperti pengguna, admin .map (grantedauthority :: getAuthority) .collect (collectors.joining (")); long now = (new date ()). getTime (); // Dapatkan validitas tanggal waktu saat ini; // waktu kedaluwarsa penyimpanan jika (ingatMe) {validitas = tanggal baru (sekarang + this.tokenValidityInmilliseconds); } else {validitas = tanggal baru (sekarang + this.tokenValidityInmillisecondSforrememberme); } return jwts.builder () // create token token.setsubject (authentication.getname ()) // atur user-oriented.claim (otoritas_key, otoritas) // tambahkan izin atribut.setExpiration (validitas) // setrechey / /pegnature.signwith (SignatureAlgorith Mm.hsey (HOMAUCE.212, Secretature /Secretature (Signaturealgorith. } // Dapatkan izin pengguna otentikasi publik getAuthentication (string token) {System.out.println ("token:"+token); Klaim klaim = jwts.parser () // Parse Token's Payload .SetsigningKey (SecretKey) .parseclaimSjws (token) .getBody (); Koleksi <? Extends Grantedauthority> Otorities = arrays.stream (klaims.get (otoritas_key) .toString (). split (",")) // Dapatkan string izin pengguna.map (SimpleGrantEdAuthority :: new) .collect (collectors.tolist ()); // Konversi elemen menjadi prinsip pengguna koleksi antarmuka yang diberikan Return New UsernamepasswordAuthenticationToken (Kepala Sekolah, "", Otoritas); } // Verifikasi apakah token itu benar boolean public validateToken (string token) {coba {jwts.parser (). SetSigningKey (SecretKey) .parseclaimsjws (token); // Verifikasi token dengan kunci kembali true; } catch (SignatureException e) {// tanda tangan Exception log.info ("tanda tangan jwt tidak valid."); log.trace ("jejak tanda tangan jwt tidak valid: {}", e); } catch (MalFormedjwtException e) {// jwt format kesalahan log.info ("token jwt tidak valid."); log.trace ("Token Trace JWT Invalid: {}", e); } Catch (ExpiredJwtException e) {// jwt kadaluwarsa log.info ("Token JWT yang kedaluwarsa."); log.trace ("Token jejak JWT yang sudah kedaluwarsa: {}", e); } catch (unsupportedjwtException e) {// jwt log.info ("token jwt yang tidak didukung."); log.trace ("Token Trace JWT yang tidak didukung: {}", e); } catch (IllegalArgumentException e) {// Parameter kesalahan pengecualian log.info ("Token JWT Compact of Handler tidak valid."); log.trace ("JWT token compact of handler adalah jejak tidak valid: {}", e); } return false; }} Menerapkan antarmuka userDetails, mewakili kelas entitas pengguna, dibungkus pada objek pengguna kami, berisi izin dan properti lainnya, dan dapat digunakan oleh Spring Security
kelas publik myUserDetails mengimplementasikan userDetails {pengguna pengguna privat; public myuserdetails (pengguna pengguna) {this.user = user; } @Override Public Collection <? Extends Grantedauthority> getAuthoritys () {List <lance> Roles = user.getRoles (); Daftar <BanteriTauthority> Otoritas = ArrayList baru <> (); StringBuilder SB = StringBuilder baru (); if (roles.size ()> = 1) {for (peran peran: peran) {otoritas.add (new SimpleGrantEdAuthority (role.getName ())); } mengembalikan otoritas; } mengembalikan otoritas; } return otoritas. } @Override public string getPassword () {return user.getPassword (); } @Override Public String getUserName () {return user.getName (); } @Override public boolean isAccountNonexpired () {return true; } @Override public boolean isAccountNonLocked () {return true; } @Override public boolean isCredentialSnonexpired () {return true; } @Override public boolean isenabled () {return true; }} Implementasikan antarmuka UserDetailsService, yang hanya memiliki satu metode untuk mendapatkan UserDetails. Kita bisa mendapatkan objek pengguna dari database, lalu membungkusnya menjadi pengguna dan mengembalikannya
@Servicepublic kelas myUserDetailsService mengimplementasikan userDetailsService {@Autowired userrepository userrepository; @Override Public UserDetails LoadUserByUserName (String S) melempar UserNamenotFoundException {// Muat objek pengguna dari database opsional <user> user = userrepository.findbyname (s); // Untuk debugging, jika nilainya ada, nama pengguna dan kata sandi adalah output. Ifpresent ((value)-> System.out.println ("Nama pengguna:"+value.getName ()+"Kata Sandi Pengguna:"+value.getPassword ())); // Jika nilainya tidak lagi, return null return baru myuserdetails (user.orelse (null)); }} Tulis filter. Jika pengguna membawa token, ia akan mendapatkan token, dan menghasilkan objek otentikasi otentikasi berdasarkan token, dan menyimpannya di SecurityContext untuk kontrol izin oleh Spring Security.
kelas publik jwtauthenticationTokenFilter memperluas genericfilterbean {private final logger log = loggerFactory.getLogger (jwttauthenticationTokenFilter.class); @Autowired private jwttokenutils tokenProvider; @Override public void dofilter (servletRequest servletRequest, servletResponse servletResponse, filterchain filterchain) melempar ioException, servletException {System.out.println ("jwttauthenticationTokenFilter"); coba {httpservletRequest httpreq = (httpservletRequest) servletRequest; String jwt = resolvetoken (httpreq); if (stringutils.hastext (jwt) && this.tokenprovider.validateToken (jwt)) {// verifikasi apakah jwt adalah otentikasi otentikasi yang benar = this.tokenprovider.getAuthentication (jwt); // Dapatkan Informasi Otentikasi Pengguna KeamananContextholder.getContext (). SetAuthentication (otentikasi); // Simpan pengguna ke SecurityContext} filterchain.dofilter (servletRequest, servletResponse); } catch (EXPERTEDJWTException e) {// jwt tidak valid log.info ("Pengecualian keamanan untuk pengguna {} - {}", e.getClaims (). getSubject (), e.getMessage ()); log.trace ("jejak pengecualian keamanan: {}", e); ((HttpservletResponse) servletResponse) .setStatus (httpservletResponse.sc_unauthorized); }} private string resolvetoken (httpservletrequest request) {string bearertoken = request.getheader (websecurityconfig.authorization_header); // Dapatkan token dari header http if (stringutils.hastext (bearertoken) && bearertoken.startswith ("bearer")) {return bearertoken.substring (7, bearertoken.length ()); // kembalikan string token dan hapus pembawa} string jwt = request.getParameter (websecurityconfig.authorization_token); // Dapatkan token dari parameter permintaan if (stringutils.hastext (jwt)) {return jwt; } return null; }} Tulis logincontroller. Pengguna mengakses /auth /login melalui nama pengguna dan kata sandi, menerimanya melalui objek Logindto, dan membuat objek otentikasi. Kode ini adalah USERNAMEPASSWORDAuthenticationToken untuk menentukan apakah objek ada. Verifikasi objek otentikasi melalui metode otentikasi AuthenticationManager. Penyedia kelas Implementasi AuthenticationManager akan memverifikasi melalui AuthenticationProvider (pemrosesan otentikasi). Penyedia default itu memanggil DaoAuthenticationProvider untuk pemrosesan otentikasi. The DaoAuthenticationProvider will obtain UserDetails through UserDetailsService (authentication information source) , if the authentication is successful, an Authention containing permissions is returned, and then set it to the SecurityContext through SecurityContextHolder.getContext().setAuthentication(), generate a token according to Authentication, and return it to the user.
@RestControllerPublic Class LogIncontroller {@Autowired private userrepository userrepository; @Autowired Private AuthenticationManager AuthenticationManager; @Autowired Private JWTTOKENUTILS JWTTOKENUTILS; @RequestMapping (value = "/auth/login", Method = requestMethod.post) Login string publik (@valid Logindto Logindto, httpservletResponse httpresponse) Lempar Kelas {// Buat Otentikasi Otentikasi Otentikasi melalui nama pengguna dan kata sandi, dan mengimplementasikan Kelas sebagai UseramPassKOKSKOKSOKSICATIONSKOKSOKSICEKSOKSICEK authenticationToken = UsernamepasswordAuthenticationToken baru (logindto.getusername (), logindto.getPassword ()); // Jika objek otentikasi tidak kosong jika (objects.nonnull (authenticationToken)) {userrepository.findbyname (authenticationToken.getPrincipal (). ToString ()) .orelsethrow (()-> Pengecualian baru ("Pengguna tidak ada")); } coba {// Verifikasi objek otentikasi melalui authenticationManager (default diimplementasikan sebagai providerManager) authentication = authenticationManager.Authenticate (authenticationToken); // Bind otentikasi ke SecurityContext SecurityContextholder.getContext (). SetAuthentication (otentikasi); // menghasilkan token token token = jwttokenutils.createToken (otentikasi, false); // Tulis token ke header http httpresponse.addheader (websecurityconfig.authorization_header, "pembawa"+token); mengembalikan "pembawa"+token; } catch (otentikasi BadCredentialSException) {lempar pengecualian baru ("kesalahan kata sandi"); }}} Tulis kelas konfigurasi keamanan, mewarisi WebSecurityConfigurerAdapter, dan mengganti metode konfigurasi
@Configuration@enableWebsecurity@enableGlobalMethodSecurity (preposteNabled = true) kelas publik websecurityconfig memperluas websecurityconfigurerAdapter {public static final string otorisasi_header = "otorisasi"; public static final string otorization_token = "access_token"; @Autowired private userDetailsService userDetailsService; @Override Protected void configure (authenticationManagerBuilder auth) melempar Exception {auth // Kustomisasi untuk mendapatkan informasi pengguna.UserDetailsService (UserDetailsService) // Setel encryption.passwordEncoder (kata sandi ()); } @Override void void configure (httpsecurity http) melempar Exception {// Mengkonfigurasi Kebijakan Akses Permintaan HTTP // Tutup CSRF dan CORS .CORS (). Disable () .CSRF (). Sesi (). Sesi tidak diperlukan sejak Token digunakan, tidak diperlukan sesi. .and () // verifikasi http revand.authorizeRequests () // Izinkan semua pengguna untuk mengakses beranda dan login.antmatchers ("/", "/auth/login"). ijinall () // semua permintaan pengguna lainnya. .and () // atur logout (). ijinAll (); // Tambahkan filter JWT di http .addfilterbefore (genericfilterbean (), usernepasswordAuthenticationFilter.class); } @Bean Public PasswordEncoder PasswordEncoder () {return bcryptpasswordEncoder baru (); } @Bean Genericfilterbean genericfilterbean genericfilterbean () {return jwttauthenticationTokenFilter () baru; }} Tulis pengontrol untuk pengujian
@RestControllerPublic kelas UserController {@PostMapping ("/Login") Public String Login () {return "Login"; } @GetMapping ("/") public string index () {return "hello"; } @GetMapping ("/UserPage") Public String httpapi () {System.out.println (SecurityContExTholder.getContext (). GetAuthentication (). GetPrincipal ()); mengembalikan "UserPage"; } @GetMapping ("/adminPage") Public String httpSuite () {return "userpage"; }}Unduh Kode Sumber Kasus (Unduh Lokal)
Meringkaskan
Di atas adalah seluruh konten artikel ini. Saya berharap konten artikel ini memiliki nilai referensi tertentu untuk studi atau pekerjaan semua orang. Jika Anda memiliki pertanyaan, Anda dapat meninggalkan pesan untuk berkomunikasi. Terima kasih atas dukungan Anda ke wulin.com.