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
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
kimi yaradılır.
@Enable
annotationları həmin obyektləri yaratmaq üçün yazılır.\
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ı.
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();
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` blo
b
,
`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
Post a Comment