Spring Restful API Security


Spring Restful Api Security OAuth2

Proyekt Linki - https://github.com/hemidsultan/group-sweethome-ders

curl java:hamid@localhost:8080/oauth/token -d grant_type=password -d username=john.doe -d password=jwtpass

java - Client Id
hamid – Client Security , Apini istifadə edən şəxsə aiddir, Developerə aiddir
localhost:8080/oauth/token – Application URL
username, pass Api ə müraciət edən müştəriyə aiddir.

Sorğunu göndəririk və aşağıdakı şəkildə JSON obyekt əldə edirik:

{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidGVzdGp3dHJlc291cmNlaWQiXSwidXNlcl9uYW1lIjoiam9obi5kb2UiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXhwIjoxNTY2NTg3NjE3LCJhdXRob3JpdGllcyI6WyJTVEFOREFSRF9VU0VSIl0sImp0aSI6IjkyOTgwNjRjLTc4ZTUtNDA4Yy05YTBhLTUwZjQzNTEyMjVhNSIsImNsaWVudF9pZCI6ImphdmEifQ.oZY5TlhvfF1TYc36njtN5A6UY7xNG7yYFVO3EANd2oU","token_type":"bearer","expires_in":43199,"scope":"read write","jti":"9298064c-78e5-408c-9a0a-50f4351225a5"}

Token – develpoerin kimliyini təsdiq edən heşlənmiş koddur. Təhlükəsizlik üçün istifadə olunur.

Burada olan Access tokeni https://jwt.io/ saytında yoxlayırıq:


scope – Bu restful api ilə bazaya yazma və oxuma əməliyyatlarını aparmaq mümkündür. Developerə aiddir.
authorities - isifadəçiyə aiddir.


@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter



@Value("${security.signing-key}")
private String signingKey;

@Value("${security.encoding-strength}")
private Integer encodingStrength;

@Value("${security.security-realm}")
private String securityRealm;

@Value annotaion-u application.properties faylındakı məlumatları çəkərək uyğun dəyişlənlərə mənimsədir.

@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}

AuthenticationManager – istifadəçini autentifikasiya eləmək üçün, həmişəm çağrılan classdır. Security bütün işlərini bu method vasitəsilə görür.
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(signingKey);
return converter;
}

Yuxarda gördüyümüz Tokeni genersiya edən Methoddur. Tokeni signingkey vasitəsilə generasiya edir.

@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}

Tokeni check eləmək üçün bir yerdə saxlanması mütləqdir. Buna görə yuxardakı Methoddan istifadə olunur.

@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}

TokenServices – Tokenin manager i kimi başa düşülür. Burada tokenlə bağlı əməliyyatlar yerinə yetirilir.
Yuxarda aldığımız tokenin bir müddəti olur. Bu müddət bitdikdən sonra, token istifadəsi mümkün olmur. SetSupportRefreshToken methodu bu müddətin yenilənməsinə xidmət edir.

Elan etdiyimiz hər bir obyekt aşağıdakı methodda istifadə olunur:

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.httpBasic()
.realmName(securityRealm)
.and()
.csrf()
.disable();

}

Bu method STATELESS bir session yaradır. Restful əsas xüsusiyyəti budur. Yəni serverdə developer haqqında heç bir məlumat olmur. Server developeri tokenə görə tanıyır.

DatasourceConfig.java

SQL skriptlərini işə salmaq üçün istifadə olunur. Bu proyektdə table və table-ə data daxil etmək üçün skriptlər mövcuddur.

SecurityConfig – istifadəçini yoxlayır.
AuthorizationServerConfig – developeri yoxlayır.
ResourceServiceConfig – developerin müraciət etdiyi url i yoxlayır.

ResourceServiceConfig və AuthorizationServerConfig classları uyğun olaraq

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter

kimi yaradılır.

@Enable annotationları həmin obyektləri yaratmaq üçün yazılır.\

AuthorizationServer tərəfindən istifadəçinin kimliyi təsdiq olunur.
ResouceServiceConfig hansı resursa müraciət olunursa onu təsdiqləyir.

Sorğu göndərərkən AuthorizationServer işə düşür. Client id və Client secret yoxlayır və hara müraciət elədiyini təyin edir.

AuthorizationServerConfig.java

@Value("${security.jwt.client-id}")
private String clientId;
@Value("${security.jwt.client-secret}")
private String clientSecret;
@Value("${security.jwt.grant-type}")
private String grantType;
@Value("${security.jwt.scope-read}")
private String scopeRead;
@Value("${security.jwt.scope-write}")
private String scopeWrite = "write";
@Value("${security.jwt.resource-ids}")
private String resourceIds;
@Autowired
private TokenStore tokenStore;
@Autowired
private JwtAccessTokenConverter accessTokenConverter;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private PasswordEncoder passwordEncoder;

Obyektlərin set olunması.

@Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
configurer
.inMemory()
.withClient(clientId)
.secret(passwordEncoder.encode(clientSecret))
.authorizedGrantTypes(grantType)
.scopes(scopeRead, scopeWrite)
.resourceIds(resourceIds);
}

Bu method yaddaşda client id və heşlənmiş client secret i, yoxlayır və həmin şəxs yazma və oxuma scope larına malikdir. Bu şəxs bu resourceIds ə müraciət edə bilər. application.properties faylında bu resource id göstərilir. @Value vasitəsi ilə set olunur.

ResourceServiceConfig.java

@Autowired
private ResourceServerTokenServices tokenServices;
@Value("${security.jwt.resource-ids}")
private String resourceIds;

Obyektlərin set olunması
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(resourceIds).tokenServices(tokenServices);
}

Burada resource id təyin olunur və həmin id bu token servisdən istifadə edəcək.

@Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.and()
.authorizeRequests()
.antMatchers("/actuator/**", "/api-docs/**").permitAll()
.antMatchers("/springjwt/**" ).authenticated();
}

Bu resursa yuxardakı filtrasiya aiddir.

.authenticated();

Resursa daxil olanlari yoxla, bu işə controller baxacaq. Alternativi olaraq ,

.antMatchers("/springjwt/**" ).hasAuthority(“USER”);

yazmaq olar.

.antMatchers("/actuator/**", "/api-docs/**").permitAll()

Bu resursun autentifikasiyaya ehtiyacı yoxdur.

.antMatchers("/springjwt/**" ).authenticated();

Bu resursa müraciət edən hər kəs autentifikasiyadan keçməlidir.

@Configuration
public class AdditionalWebConfig {

@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
Bu method bizim api nin istifadəsi üçün qıraq mənbələrə icazələri verir. Məsələn, bir application var və api də həmin application daxilində yerləşir. Yəni, controller-i mövcuddur. Başqa bir application ilə bu application dakı apini istifadə eləmək istəsək problemlə rastlaşacağıq.

Bu method vasitəsilə elə problemlərin qarşısı alınır. Burada Credentials qəbul etdiyini, Origin, Header, Method u istifadəsini icazə verdiyini təsdiqləyir. Ən axırda hansı url dən etibarən bu qaydaların tətbiq olunacağını təyin edir.


Bunlar hamısı , OAuth2 protokoluna uyğun olaraq işləyir. Kodlarda da OAuth2 kitabxanasından istifadə olunur.

pom.xml

Application-ı işlətdiyimiz kompüterdə mysql və ya digər Database-lər olmadıqda, H2 kitabxanasından istifadə etmək mümkündür. H2 kitabxanası application-ın özündə Database yaradır və ondan istifadə edir.


Error

OAuth2 Resource – əgər application-da heç bir Resurs Serveri olmasa, default olaraq OAuth2 Resource Serveri yaranır. Bu da bizim geneasiya etdiyimiz tokeni əhatə etməyə bilər.

Spring Rest Security OAuth2 – With DB

Əldə etdiyimiz tokeni DB – da saxlatmaq üçün ilk öncə ona uyğun table yaratmalıyıq.

CREATE TABLE `oauth_access_token` (
`authentication_id` varchar(255) NOT NULL,
`token_id` varchar(255),
`token` blob,
`user_name` varchar(255),
`client_id` varchar(255),
`authentication` blob,
`refresh_token` varchar(255)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `oauth_access_token`
ADD PRIMARY KEY (`authentication_id`);


CREATE TABLE `oauth_refresh_token` (
`token_id` varchar(255) NOT NULL,
`token` blob NOT NULL,
`authentication` blob NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Bu query – ləri run etdikdən sonra SecurityConfig faylında bəzi dəyişiklər aparmalıyıq.

İlk öncə bu faylda yerləşən tokenStore funksiyasını aşağıdakı şəkildə dəyişməliyik

@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(defDatasource);
}

DB-ya tokeni əlavə eləmək üçün JdbcTokenStore -dan istifadə edirik.
Əvvəlkindən fərqli olaraq, JdbcTokenStore accessTokenConverter i parametr olaraq qəbul etmir. Burada parametr olaraq DataSource tipində olan obyekt göndərmək lazımdır.
Spring özü arxada DataSource tipində obyekt yaradır, SecurityConfig faylında

@Autowired
DataSource defDatasource;

edərək həmin obyekti öz dəyişənimizə set edə bilərik.
Daha sonra Postman vasitəsilə tokenimizi əldə edə bilərik.

Comments

Popular posts from this blog

Validation for AZ phone numbers with RegEx in Java

Java necə işləyir, kod nədir ?