Spring Security Kloonige https://bitbucket.org/mkalmo/icd0011sec repo ja importige see IDE-sse. 1. Uurige sessiooniga rakenduse käitumist: a) käivitage rakendus; b) avage Chrome brauser ja selle arendaja tööriistade (F12) alt Network sakk; c) laadige aadress http://localhost:8080/ veenduge, et küpsist ei seata; d) laadige aadress http://localhost:8080/count veenduge, et küpsis seatakse; e) värskendage lehte ja veenduge, et seatud küpsis saadetakse koos päringuga; f) kustutage küpsis. Chrome brauseris aadressiriba ees olevale hüüumärgile klikates avaneb vastav võimalus; g) värskendage lehte ja veenduge, et küpsist ei saadeta ja samas seatakse uus küpsis. 2. Tehke nii, et päringud "/api"-ga algavale aadressile (nt. http://localhost:8080/api/home) vajavad autentimist. Selleks lisage konfiguratsiooni: http.authorizeRequests().antMatchers("/api/**").authenticated(); Kontrollige, et päring aadressile /api/home tagastab koodi 403. Kontrollige, et test apiUrlsNeedAuthentication() (failis src/test/java/test/SecurityIntegrationTest.java) läheb läbi. 3. Lisage erand aadressi /api/home kohta http.authorizeRequests().antMatchers("/api/home").permitAll() NB! Erand peab olema enne üldist reeglit. Kontrollige, et erand toimib (päring õnnestub). Kontrollige, et test apiHomeDoesNotNeedAuthentication() läheb läbi. 3. Tehke nii, et rakendus näitaks sisselogimise vormi, kui ligipääs puudub. Selleks lisage konfiguratsiooni: http.formLogin(); Kontrollige, et aadressile /api/info minnes saate koodi 302 ja teid suunatakse sisselogimise vormile. Kontrollige, et test redirectsToLoginForm() läheb läbi. 4. Lisage kasutaja nimega "user", salasõnaga "123" ja rolliga "USER" ning kasutaja nimega "admin", salasõnaga "123" ja rollidega "USER" ja "ADMIN" builder.inMemoryAuthentication() .passwordEncoder(new BCryptPasswordEncoder()) .withUser("user") .password("$2a$10...") .roles("USER") .and() .withUser("admin") .password("$2a$10...") .roles("USER", "ADMIN"); Räsi arvutamise näide on testide klassis test.PasswordEncoderTest. Minge aadressile /api/info. Rakendus peaks näitama sisselogimise vormi. Logige sisse kasutajaga "user". Teid peaks tagasi suunatama aadressile /api/info. Kontrollige, et test canLoginWithCorrectPassword() läheb läbi. 5. Lisage piirang, et aadressilt /api/admin/** olevat infot näiks ainult kasutaja kellel on "ADMIN" roll. http.authorizeRequests() .antMatchers("/api/admin/**").hasRole("ADMIN") Kontrollige, et sisse logides ei näe kasutaja infot aadressilt /api/admin/info aga admin näeb. Kontrollige, et test adminCanSeeMoreInfo() läheb läbi. 6. Lisage väljalogimise võimalus http.logout().logoutUrl("/api/logout"); Väljalogimiseks tuleb teha post päring aadressile "/api/logout". Toorikus on kaasas vorm (/logout-form), mis vastava päringu teeb. Kontrollige, et ka test canLogOut() läheb läbi. Järgmiste ülesannete käigus muudate raamistiku käitumise sobivaks api-põhisele rakendusele. 7. Tehke nii, ligipääsu puudumisel ei suunata sisselogimise vormile vaid tagastatakse vea kood (401). http.exceptionHandling() .authenticationEntryPoint(new ApiEntryPoint()); http.exceptionHandling() .accessDeniedHandler(new ApiAccessDeniedHandler()); Kontrollige, et test doesNotShowLoginForm() läheb läbi. 8. Tehke nii, et väljalogimine ei suuna kuhugi ja tagastab koodi 200 http.logout().logoutSuccessHandler(new ApiLogoutSuccessHandler()); Kontrollige, et test logOutDoesNotRedirect() läheb läbi. 9. Lisage sisselogimise teenus aadressiga /api/login. Vajalik info saadetakse Json kujul { "userName": "user", "password": "secret" } Konfiguratsiooniks on vajalik lisada erand sisselogimise aadressi kohta http.authorizeRequests().antMatchers("/api/login").permitAll() lülitada välja csrf kontroll http.csrf().disable(); lisada filter, mis reageerib aadressile "/api/login" päringule. var apiLoginFilter = new ApiAuthenticationFilter( authenticationManager(), "/api/login"); http.addFilterAfter(apiLoginFilter, LogoutFilter.class); Lisaks peate kirjutama klassi ApiAuthenticationFilter vajaliku koodi (kuhu ja mida kirjutada on kommentaarides kirjas) Kui sisend ei ole oodatud info oodatud kujul, saab sellest märku anda erindiga BadCredentialsException. Kontrollige Postman-iga ja testiga canLoginWithJsonRequest(). 10. Aadressilt /api/users/ on võimalik küsida konkreetse kasutaja infot. Tehke nii, et näidatakse infot vaid sisseloginud kasutaja kohta. Nt. kui olen sisse loginud kasutajana "user" ja pöördun aadressile /api/users/user, siis näen vastavat infot, kui aga pöördun aadressile /api/users/alice, siis saan vea (http koodiga 401) Konfiguratsiooniks on vaja annotatsiooni Spring-i konfiguratsiooni klassil, mis laeb kontrollerid @EnableGlobalMethodSecurity(prePostEnabled = true) ja annotatsiooni kontrolleri meetodil @PreAuthorize("#userName == authentication.name") Kontrollige Postman-iga ja testiga userCanSeeOnlyOwnInfo(). 11. Pange rakendus kasutama Jwt-põhist autoriseerimist. ApiAuthenticationFilter-i asemel peaks kasutama JwtAuthenticationFilter-it. See filter pärineb ApiAuthenticationFilter-ist ja kasutab ära viimase attemptAuthentication() meetodi, mida täiendasite punktis 9. Lisaks genereerib see token-i ja paneb selle Http vastuse päisesse. See filter vajab ka võtit krüpteerimiseks. Võti on application.properties failis ja selle saate Spring-i konkfiguratsiooni süstida. @Value("${jwt.signing.key}") private String jwtKey; Lisaks peate lisama filtri, mis otsib Http päisest token-it ja kui leiab selle, autoriseerib päringu. var jwtAuthFilter = new JwtAuthorizationFilter(authenticationManager(), jwtKey); http.addFilterBefore(jwtAuthFilter, LogoutFilter.class); Nüüd võib sessiooni kasutamise välja lülitada, kuna ligipääsu kontroll on lahendatud ilma selleta. http.sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); Kontrollige Postman-iga ja testiga canAccessWithJwtToken(). Lahendused ja seletused: https://youtu.be/_Dd_c1yIstg