Kuidas seadistada Java Spring Boot JWT autoriseerimist ja autentimist

Eelmisel kuul oli mul võimalus JWT-autentimine kõrvalprojekti jaoks juurutada. Olen varem töötanud JWT-ga rubriigis Rails, kuid see oli minu esimene kord kevadel.

Selles postituses püüan selgitada, mida olen õppinud ja oma projektis rakendanud, et jagada oma kogemusi ja loodetavasti mõnda inimest aidata.

Alustuseks vaatame kiiresti JWT teooriat ja selle toimimist. Seejärel uurime, kuidas seda rakenduses Spring Boot rakendada.

JWT põhitõed

JWT või JSON-i veebimärgid (RFC 7519) on standard, mida kasutatakse enamasti REST-i API-de turvamiseks. Hoolimata suhteliselt uuest tehnoloogiast, kogub see kiiret populaarsust.

JWT-i autentimisprotsessis saadab kasutajaliides (klient) kõigepealt enda autentimiseks mõned mandaadid (meie puhul kasutajanimi ja parool, kuna töötame veebirakenduse kallal).

Seejärel kontrollib server (meie puhul Spring-rakendus) neid mandaate ja kui need kehtivad, genereerib JWT ja tagastab need.

Pärast seda sammu kliendil on pakkuda selle märgise taotluse tema Luba päises "Kandja TOKEN" abil. Tagumine osa kontrollib selle loa kehtivust ja volitab või lükkab taotlused tagasi. Luba võib ka salvestada kasutajarolle ja volitada taotlusi antud volituste alusel.

Rakendamine

Vaatame nüüd, kuidas saame JWT sisselogimis- ja salvestusmehhanismi reaalses kevade rakenduses rakendada.

Sõltuvused

Allpool näete Maveni sõltuvuste loendit, mida meie näidiskood kasutab. Pange tähele, et põhisõltuvusi nagu Spring Boot ja Hibernate ei ole selles ekraanipildis.

Kasutajate salvestamine

Alustame kasutajate turvaliseks salvestamiseks kontrollerite loomisest ja nende kasutajanime ja parooli alusel autentimiseks.

Meil on mudeliüksus nimega Kasutaja. See on lihtne üksuse klass, mis kaardistatakse tabelis USER . Sõltuvalt teie rakendusest saate kasutada mis tahes omadusi.

Kasutajate salvestamiseks on meil ka lihtne UserRepository klass. Peame meetodi findByUsername alistama, kuna kasutame seda autentimisel.

public interface UserRepository extends JpaRepository{ User findByUsername(String username); }

Me ei tohiks kunagi andmebaasis ladustada selgesõnalisi paroole, sest paljud kasutajad kasutavad mitme saidi jaoks sama parooli.

Räsimisalgoritme on palju erinevaid, kuid kõige sagedamini kasutatakse BCrypt ja see on soovitatav meetod turvaliseks räsimiseks. Teema kohta lisateavet leiate sellest artiklist.

@Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); }

Parooli räsimiseks määratleme rakenduses @SpringBootApplication BCrypt uba ja märkige põhiklass järgmiselt:

Kui vajame parooli räsimist, kutsume selle oa meetodeid.

Kasutajate salvestamiseks vajame ka UserControllerit. Loome kontrolleri, annoteerime selle @RestControlleriga ja määrame vastava kaardistuse.

Meie rakenduses salvestame kasutaja DTO-objekti põhjal, mis edastatakse esiosast. Kasutajaobjekti saate edastada ka saidil @RequestBody .

Pärast DTO-objekti läbimist krüpteerime paroolivälja varem loodud BCrypti oa abil. Seda võiksite teha ka kontrolleris, kuid parem on see loogika teenindusklassi panna.

@Transactional(rollbackFor = Exception.class) public String saveDto(UserDto userDto) { userDto.setPassword(bCryptPasswordEncoder.encode(userDto.getPassword())); return save(new User(userDto)).getId(); }

Autentimisfilter

Vajame autentimist, et veenduda, et kasutaja on tegelikult see, kes ta väidab end olevat. Selle saavutamiseks kasutame klassikalist kasutajanime / parooli paari.

Autentimise juurutamiseks toimige järgmiselt.

  1. Looge meie autentimisfilter, mis laiendab UsernamePasswordAuthenticationFilterit
  2. Looge turbekonfiguratsiooni klass, mis laiendab WebSecurityConfigurerAdapterit, ja rakendage filter

Siin on meie autentimisfiltri kood - nagu võite teada, on filtrid kevade turvalisuse selgroog.

Vaatame seda koodi samm-sammult üle.

See klass laiendab kasutajatunnust PassPasswordAuthenticationFilter, mis on Spring Security paroolide autentimise vaikeklass. Laiendame seda oma kohandatud autentimisloogika määratlemiseks.

Helistame oma konstruktoris meetodile setFilterProcessesUrl . See meetod määrab vaikimisi sisselogimise URL-i esitatud parameetriks.

Selle rea eemaldamisel loob Spring Security vaikimisi lõpp-punkti “/ login” . See määratleb meie jaoks sisselogimise lõpp-punkti, mistõttu me ei määra oma kontrolleris sisselogimise lõpp-punkti selgesõnaliselt.

Selle rea järel on meie sisselogimise lõpp-punkt / api / services / controller / user / login . Selle funktsiooni abil saate püsida oma lõpp-punktidega kooskõlas.

Me alistada attemptAuthentication ja successfulAuthentication meetodid UsernameAuthenticationFilter klassi.

Funktsioon tryAuthentication töötab, kui kasutaja proovib meie rakendusse sisse logida. See loeb mandaadid, loob neist kasutaja POJO ja kontrollib seejärel autentimiseks mandaate.

Edastame kasutajanime, parooli ja tühja loendi. Tühi loend tähistab asutusi (rolle) ja me jätame selle nii, nagu meil pole oma rakenduses veel ühtegi rolli.

Kui autentimine on edukas, töötab edukalt autentimise meetod. Selle meetodi parameetrid edastab Spring Security kulisside taga.

The attemptAuthentication method returns an Authentication object that contains the authorities we passed while attempting.

We want to return a token to user after authentication is successful, so we create the token using username, secret, and expiration date. We need to define the SECRET and EXPIRATION_DATE now.

We create a class to be a container for our constants. You can set the secret to whatever you want, but the best practice is making the secret key as long as your hash. We use the HS256 algorithm in this example, so our secret key is 256 bits/32 chars.

The expiration time is set to 15 minutes, because it is the best practice against secret key brute-forcing attacks. The time is in milliseconds.

We have prepared our Authentication filter, but it is not active yet. We also need an Authorization filter, and then we will apply them both through a configuration class.

This filter will check the existence and validity of the access token on the Authorization header. We will specify which endpoints will be subject to this filter in our configuration class.

Authorization Filter

The doFilterInternal method intercepts the requests then checks the Authorization header. If the header is not present or doesn’t start with “BEARER”, it proceeds to the filter chain.

If the header is present, the getAuthentication method is invoked. getAuthentication verifies the JWT, and if the token is valid, it returns an access token which Spring will use internally.

This new token is then saved to SecurityContext. You can also pass in Authorities to this token if you need for role-based authorization.

Our filters are ready, and now we need to put them into action with the help of a configuration class.

Configuration

We annotate this class with @EnableWebSecurity and extend WebSecurityConfigureAdapter to implement our custom security logic.

We autowire the BCrypt bean that we defined earlier. We also autowire the UserDetailsService to find the user’s account.

The most important method is the one which accepts an HttpSecurity object. Here we specify the secure endpoints and filters that we want to apply. We configure CORS, and then we permit all post requests to our sign up URL that we defined in the constants class.

You can add other ant matchers to filter based on URL patterns and roles, and you can check this StackOverflow question for examples regarding that. The other method configures the AuthenticationManager to use our encoder object as its password encoder while checking the credentials.

Testing

Let’s send a few requests to test if it works properly.

Here we send a GET request to access a protected resource. Our server responds with a 403 code. This is the expected behavior because we haven’t provided a token in the header. Now let’s create a user:

To create a user, we send a post request with our User DTO data. We will use this user to login and get an access token.

Great! We got the token. After this point, we will use this token to access protected resources.

We provide the token in the Authorization header and we are now allowed access to our protected endpoint.

Conclusion

In this tutorial I have walked you through the steps I took when implementing JWT authorization and password authentication in Spring. We also learned how to save a user securely.

Tänan teid lugemast - loodan, et see oli teile kasulik. Kui olete huvitatud rohkem sellise sisu lugemisest, tellige julgelt minu ajaveeb //erinc.io. :)