diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..92ab0a2
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..aa00ffa
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000..fd0296b
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..d31b37a
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5f46d99
--- /dev/null
+++ b/README.md
@@ -0,0 +1,88 @@
+Было решено написать эту библиотеку по причине того, что документация Spring Security не удовлетворяла нашим требованиям.
+Библиотека протестирована на Java 11.
+
+Для реализации аутентификации OAuth2 VK ID в Вашем приложении сперва необходимо зарегистрировать его в личном кабинете разработчика VK:
+
+1. Перейти по ссылке https://id.vk.com/about/business/go
+2. Нажать "*Добавить приложение*"
+3. Ввести название Вашего приложения и среди типов платформ включить как минимум "*Web*".
+4. (Опционально) Загрузить изображение приложения, которое будет отображаться в личном кабинете пользователя
+5. Нажать "*Далее*"
+6. Заполнить поля "*Базовый домен*" и "*Доверенный Redirect URL*". Подсказки о них есть рядом с полями ввода
+7. Подтвердить действие SMS-кодом, если требуется (или другие способы подтверждения личности в зависимости от Ваших настроек VK)
+8. На шаге "*Способы быстрого входа в web-приложении*" можно выбрать все три способа.
+9. (Опционально) На шаге "*Интеграция элементов*" можно настроить внешний вид по своему вкусу. Вставку кода можно пропустить.
+
+
+Вы окажетесь на странице https://id.vk.com/about/business/go/accounts/{some_id}}/apps/{your_app_id}/edit
+Отсюда Вам понадобятся ID приложения и Redirect URL.
+
+Дальше можно переходить к работе непосредственно с библиотекой:
+
+Сначала Вам необходимо в Вашем приложении создать бин класса VkSettings, передав ему две настройки:
+
+- clientId
+- redirectUri
+
+Где эти две настройки - это сохраненные Вами ранее ID приложения и Redirect URL соответственно.
+
+_ПРИМЕЧАНИЕ: Далее вся конфигурация будет строиться на создании бинов Spring.
+В теории можно попробовать обойтись и без создания бинов этих классов и просто вручную создавать их экземпляры в коде,
+но такой вариант не проверялся на работоспособность._
+
+Пример:
+```
+
+
+
+
+```
+
+Далее необходимо зарегистрировать в Вашем приложении два фильтра:
+
+- VkBeginAuthFilter
+- VkEndAuthFilter
+
+Оба фильтра нужно зарегистрировать в качестве бинов Spring, передавая им в параметры один и тот же экземпляр класса VkSettings.
+
+*springSecurity.xml:*
+```
+
+
+
+
+
+
+```
+
+Второй фильтр должен срабатывать после первого. Этого можно достичь следующим образом
+
+*springSecurity.xml:*
+```
+
+
+
+```
+
+И так же нужно зарегистрировать первый фильтр для прослушивания запросов
+
+*web.xml:*
+```
+
+ vkBeginAuthFilter
+ org.springframework.web.filter.DelegatingFilterProxy
+
+
+ vkBeginAuthFilter
+ /oauth2/authorization/vk
+
+```
+где "*/oauth2/authorization/vk*" - адрес, по которому нужно будет переходить для начала процесса аутентификации.
+
+В результате успешной аутентификации возвращается объект VkUser с полями, заполненными информацией пользователя VK.
+
+?????
+
+PROFIT!!
+
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..6230fec
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,123 @@
+
+
+
+ 4.0.0
+
+ ru.simplex2
+ vkid_spring_security
+ 1.0-SNAPSHOT
+
+ vkid_spring_security
+ A simple vkid_spring_security.
+
+ https://simplex-software.ru/
+
+
+ UTF-8
+
+
+ 11
+
+
+
+
+
+ ch.qos.logback
+ logback-classic
+ 1.4.5
+
+
+ ch.qos.logback
+ logback-core
+ 1.4.5
+
+
+
+ javax.servlet
+ javax.servlet-api
+ 4.0.1
+ provided
+
+
+
+ org.springframework.security
+ spring-security-core
+ 5.6.1
+
+
+ org.springframework.security
+ spring-security-config
+ 5.6.1
+
+
+ org.springframework.security
+ spring-security-oauth2-client
+ 5.6.1
+
+
+ org.springframework.security
+ spring-security-oauth2-jose
+ 5.6.1
+
+
+
+ junit
+ junit
+ 3.8.1
+
+
+
+
+ src/main/java
+
+
+
+ maven-clean-plugin
+ 3.1.0
+
+
+ maven-site-plugin
+ 3.7.1
+
+
+ maven-project-info-reports-plugin
+ 3.0.0
+
+
+
+ maven-resources-plugin
+ 3.0.2
+
+
+ maven-compiler-plugin
+ 3.8.0
+
+
+ maven-surefire-plugin
+ 2.22.1
+
+
+ maven-jar-plugin
+ 3.0.2
+
+
+ maven-install-plugin
+ 2.5.2
+
+
+ maven-deploy-plugin
+ 2.8.2
+
+
+
+
+
+
+
+
+ maven-project-info-reports-plugin
+
+
+
+
diff --git a/src/main/java/ru/simplex2/vkid_spring_security/App.java b/src/main/java/ru/simplex2/vkid_spring_security/App.java
new file mode 100644
index 0000000..5091408
--- /dev/null
+++ b/src/main/java/ru/simplex2/vkid_spring_security/App.java
@@ -0,0 +1,13 @@
+package ru.simplex2.vkid_spring_security;
+
+/**
+ * Hello world!
+ *
+ */
+public class App
+{
+ public static void main( String[] args )
+ {
+ System.out.println( "Hello World!" );
+ }
+}
diff --git a/src/main/java/ru/simplex2/vkid_spring_security/vk/VkAuthenticationProvider.java b/src/main/java/ru/simplex2/vkid_spring_security/vk/VkAuthenticationProvider.java
new file mode 100644
index 0000000..0c4cc16
--- /dev/null
+++ b/src/main/java/ru/simplex2/vkid_spring_security/vk/VkAuthenticationProvider.java
@@ -0,0 +1,19 @@
+package ru.simplex2.vkid_spring_security.vk;
+
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+
+public class VkAuthenticationProvider implements AuthenticationProvider {
+
+ @Override
+ public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+ authentication.setAuthenticated(true);
+ return authentication;
+ }
+
+ @Override
+ public boolean supports(Class> authentication) {
+ return authentication.equals(VkOAuth2AuthenticationToken.class);
+ }
+}
diff --git a/src/main/java/ru/simplex2/vkid_spring_security/vk/VkBeginAuthFilter.java b/src/main/java/ru/simplex2/vkid_spring_security/vk/VkBeginAuthFilter.java
new file mode 100644
index 0000000..a8c2add
--- /dev/null
+++ b/src/main/java/ru/simplex2/vkid_spring_security/vk/VkBeginAuthFilter.java
@@ -0,0 +1,68 @@
+package ru.simplex2.vkid_spring_security.vk;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.crypto.codec.Hex;
+import org.springframework.security.crypto.keygen.BytesKeyGenerator;
+import org.springframework.security.crypto.keygen.StringKeyGenerator;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.security.SecureRandom;
+
+public class VkBeginAuthFilter implements Filter {
+ private static final Logger LOG = LoggerFactory.getLogger(VkBeginAuthFilter.class);
+ private static final int STATE_KEY_LENGTH = 36;
+ private static final SecureRandom random = new SecureRandom();
+ private VkSettings vkSettings;
+
+ public VkBeginAuthFilter(VkSettings vkSettings) {
+ this.vkSettings = vkSettings;
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException {
+ HttpServletRequest httpServletRequest = (HttpServletRequest) request;
+ LOG.debug("REQUEST URI: " + httpServletRequest.getRequestURI());
+ LOG.debug("PARAMETERS MAP: " + request.getParameterMap().toString());
+ HttpServletResponse httpServletResponse = (HttpServletResponse) response;
+ StringKeyGenerator keyGen = () -> {
+ BytesKeyGenerator bytesKeyGen = new BytesKeyGenerator() {
+ @Override
+ public int getKeyLength() {
+ return STATE_KEY_LENGTH;
+ }
+
+ @Override
+ public byte[] generateKey() {
+ byte[] bytes = new byte[STATE_KEY_LENGTH];
+ random.nextBytes(bytes);
+ return bytes;
+ }
+ };
+ return new String(Hex.encode(bytesKeyGen.generateKey()));
+ };
+
+ String state = keyGen.generateKey();
+ HttpSession httpSession = httpServletRequest.getSession();
+ httpSession.setAttribute(VkEndAuthFilter.STATE_ATTRIBUTE_KEY, state);
+ httpServletResponse.sendRedirect("https://id.vk.ru/authorize?response_type=code&client_id=" + vkSettings.getClientId()
+ + "&scope=email&redirect_uri=" + vkSettings.getRedirectUri()
+ + "&code_challenge_method=S256&code_challenge=" + vkSettings.generateCodeChallenge(vkSettings.generateCodeVerifier())
+ + "&state=" + state);
+ }
+
+ public VkSettings getVkSettings() {
+ return vkSettings;
+ }
+
+ public void setVkSettings(VkSettings vkSettings) {
+ this.vkSettings = vkSettings;
+ }
+}
diff --git a/src/main/java/ru/simplex2/vkid_spring_security/vk/VkEndAuthFilter.java b/src/main/java/ru/simplex2/vkid_spring_security/vk/VkEndAuthFilter.java
new file mode 100644
index 0000000..c02179a
--- /dev/null
+++ b/src/main/java/ru/simplex2/vkid_spring_security/vk/VkEndAuthFilter.java
@@ -0,0 +1,104 @@
+package ru.simplex2.vkid_spring_security.vk;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.security.authentication.ProviderManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.client.RestTemplate;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class VkEndAuthFilter extends AbstractAuthenticationProcessingFilter {
+ private static final Logger LOG = LoggerFactory.getLogger(VkEndAuthFilter.class);
+
+ private static final String DEFAULT_PROCESSING_URL = "/login/oauth2/code/vk";
+ public static final String STATE_ATTRIBUTE_KEY = "ru.simplex2.vkid.state";
+ private RestTemplate restTemplate = new RestTemplate();
+ private AuthenticationManager authenticationManager;
+ private VkSettings vkSettings;
+
+ public VkEndAuthFilter(VkSettings vkSettings){
+ this(DEFAULT_PROCESSING_URL, vkSettings);
+ }
+
+ public VkEndAuthFilter(String defaultFilterProcessesUrl, VkSettings vkSettings) {
+ this(defaultFilterProcessesUrl, new ProviderManager(new VkAuthenticationProvider()), vkSettings);
+ }
+
+ public VkEndAuthFilter(AuthenticationManager authenticationManager, VkSettings vkSettings) {
+ this(DEFAULT_PROCESSING_URL, authenticationManager, vkSettings);
+ }
+
+ public VkEndAuthFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager, VkSettings vkSettings) {
+ super(defaultFilterProcessesUrl, authenticationManager);
+ this.authenticationManager = authenticationManager;
+ this.vkSettings = vkSettings;
+ }
+
+ public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException {
+ Map parameterMap = request.getParameterMap();
+ String stateFromSession = request.getSession().getAttribute(STATE_ATTRIBUTE_KEY).toString();
+ if (!stateFromSession.equals(parameterMap.get("state")[0])) {
+ String errorMessage = "Wrong state!";
+ response.sendError(400, errorMessage);
+ throw new AuthenticationServiceException(errorMessage);
+ }
+
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+ MultiValueMap body = new LinkedMultiValueMap<>();
+ body.add("grant_type", "authorization_code");
+ body.add("code", parameterMap.get("code")[0]);
+ body.add("code_verifier", vkSettings.getCodeVerifier());
+ body.add("client_id", vkSettings.getClientId());
+ body.add("device_id", parameterMap.get("device_id")[0]);
+ body.add("redirect_uri", vkSettings.getRedirectUri());
+ body.add("state", stateFromSession);
+
+ HttpEntity> requestEntity = new HttpEntity<>(body, headers);
+ ResponseEntity