В этой главе вы подробно узнаете о механизмах Spring Boot Security и OAuth2 с JWT.
Сервер авторизации
Сервер авторизации является высшим архитектурным компонентом для безопасности Web API. Сервер авторизации действует как точка авторизации централизации, которая позволяет вашим приложениям и конечным точкам HTTP определять функции вашего приложения.
Ресурсный сервер
Resource Server — это приложение, которое предоставляет токен доступа клиентам для доступа к конечным точкам HTTP Resource Server. Это коллекция библиотек, которая содержит конечные точки HTTP, статические ресурсы и динамические веб-страницы.
OAuth2
OAuth2 — это структура авторизации, которая позволяет приложению Web Security получать доступ к ресурсам с клиента. Чтобы создать приложение OAuth2, нам нужно сосредоточиться на типе предоставления (код авторизации), идентификаторе клиента и секрете клиента.
JWT Token
JWT Token — это JSON Web Token, используемый для представления требований, защищенных между двумя сторонами. Вы можете узнать больше о токене JWT на www.jwt.io/ .
Теперь мы собираемся создать приложение OAuth2, которое позволяет использовать Сервер авторизации, Ресурсный сервер с помощью токена JWT.
Вы можете использовать следующие шаги для реализации Spring Boot Security с токеном JWT путем доступа к базе данных.
Во-первых, нам нужно добавить следующие зависимости в наш файл конфигурации сборки.
Пользователи Maven могут добавить следующие зависимости в ваш файл pom.xml.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency>
Пользователи Gradle могут добавить следующие зависимости в файл build.gradle.
compile('org.springframework.boot:spring-boot-starter-security') compile('org.springframework.boot:spring-boot-starter-web') testCompile('org.springframework.boot:spring-boot-starter-test') testCompile('org.springframework.security:spring-security-test') compile("org.springframework.security.oauth:spring-security-oauth2") compile('org.springframework.security:spring-security-jwt') compile("org.springframework.boot:spring-boot-starter-jdbc") compile("com.h2database:h2:1.4.191")
где,
-
Spring Boot Starter Security — реализует Spring Security
-
Spring Security OAuth2 — реализует структуру OAUTH2 для включения сервера авторизации и сервера ресурсов.
-
Spring Security JWT — генерирует токен JWT для веб-безопасности
-
Spring Boot Starter JDBC — обращается к базе данных, чтобы убедиться, что пользователь доступен или нет.
-
Spring Boot Starter Web — записывает конечные точки HTTP.
-
База данных H2 — хранит пользовательскую информацию для аутентификации и авторизации.
Spring Boot Starter Security — реализует Spring Security
Spring Security OAuth2 — реализует структуру OAUTH2 для включения сервера авторизации и сервера ресурсов.
Spring Security JWT — генерирует токен JWT для веб-безопасности
Spring Boot Starter JDBC — обращается к базе данных, чтобы убедиться, что пользователь доступен или нет.
Spring Boot Starter Web — записывает конечные точки HTTP.
База данных H2 — хранит пользовательскую информацию для аутентификации и авторизации.
Полный файл конфигурации сборки приведен ниже.
<?xml version = "1.0" encoding = "UTF-8"?> <project xmlns = "http://maven.apache.org/POM/4.0.0" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.tutorialspoint</groupId> <artifactId>websecurityapp</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>websecurityapp</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-jwt</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Gradle — build.gradle
buildscript { ext { springBootVersion = '1.5.9.RELEASE' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' group = 'com.tutorialspoint' version = '0.0.1-SNAPSHOT' sourceCompatibility = 1.8 repositories { mavenCentral() } dependencies { compile('org.springframework.boot:spring-boot-starter-security') compile('org.springframework.boot:spring-boot-starter-web') testCompile('org.springframework.boot:spring-boot-starter-test') testCompile('org.springframework.security:spring-security-test') compile("org.springframework.security.oauth:spring-security-oauth2") compile('org.springframework.security:spring-security-jwt') compile("org.springframework.boot:spring-boot-starter-jdbc") compile("com.h2database:h2:1.4.191") }
Теперь в основном приложении Spring Boot добавьте аннотации @EnableAuthorizationServer и @EnableResourceServer, чтобы они выполняли роль сервера проверки подлинности и сервера ресурсов в одном приложении.
Кроме того, вы можете использовать следующий код для написания простой конечной точки HTTP для доступа к API с помощью Spring Security с помощью токена JWT.
package com.tutorialspoint.websecurityapp; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @EnableAuthorizationServer @EnableResourceServer @RestController public class WebsecurityappApplication { public static void main(String[] args) { SpringApplication.run(WebsecurityappApplication.class, args); } @RequestMapping(value = "/products") public String getProductName() { return "Honey"; } }
Используйте следующий код, чтобы определить класс POJO для хранения информации о пользователе для аутентификации.
package com.tutorialspoint.websecurityapp; import java.util.ArrayList; import java.util.Collection; import org.springframework.security.core.GrantedAuthority; public class UserEntity { private String username; private String password; private Collection<GrantedAuthority> grantedAuthoritiesList = new ArrayList<>(); public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Collection<GrantedAuthority> getGrantedAuthoritiesList() { return grantedAuthoritiesList; } public void setGrantedAuthoritiesList(Collection<GrantedAuthority> grantedAuthoritiesList) { this.grantedAuthoritiesList = grantedAuthoritiesList; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } }
Теперь используйте следующий код и определите класс CustomUser, который расширяет класс org.springframework.security.core.userdetails.User для аутентификации Spring Boot.
package com.tutorialspoint.websecurityapp; import org.springframework.security.core.userdetails.User; public class CustomUser extends User { private static final long serialVersionUID = 1L; public CustomUser(UserEntity user) { super(user.getUsername(), user.getPassword(), user.getGrantedAuthoritiesList()); } }
Вы можете создать класс @Repository для чтения информации о пользователе из базы данных и отправки ее в пользовательскую службу пользователя, а также для добавления предоставленных прав доступа «ROLE_SYSTEMADMIN».
package com.tutorialspoint.websecurityapp; import java.sql.ResultSet; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.stereotype.Repository; @Repository public class OAuthDao { @Autowired private JdbcTemplate jdbcTemplate; public UserEntity getUserDetails(String username) { Collection<GrantedAuthority> grantedAuthoritiesList = new ArrayList<>(); String userSQLQuery = "SELECT * FROM USERS WHERE USERNAME=?"; List<UserEntity> list = jdbcTemplate.query(userSQLQuery, new String[] { username }, (ResultSet rs, int rowNum) -> { UserEntity user = new UserEntity(); user.setUsername(username); user.setPassword(rs.getString("PASSWORD")); return user; }); if (list.size() > 0) { GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_SYSTEMADMIN"); grantedAuthoritiesList.add(grantedAuthority); list.get(0).setGrantedAuthoritiesList(grantedAuthoritiesList); return list.get(0); } return null; } }
Вы можете создать класс обслуживания подробных пользовательских данных, который расширяет org.springframework.security.core.userdetails.UserDetailsService, чтобы вызывать класс репозитория DAO, как показано.
package com.tutorialspoint.websecurityapp; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @Service public class CustomDetailsService implements UserDetailsService { @Autowired OAuthDao oauthDao; @Override public CustomUser loadUserByUsername(final String username) throws UsernameNotFoundException { UserEntity userEntity = null; try { userEntity = oauthDao.getUserDetails(username); CustomUser customUser = new CustomUser(userEntity); return customUser; } catch (Exception e) { e.printStackTrace(); throw new UsernameNotFoundException("User " + username + " was not found in the database"); } } }
Затем создайте класс @configuration, чтобы включить веб-безопасность, определив кодировщик пароля (BCryptPasswordEncoder) и определив bean-компонент AuthenticationManager. Класс конфигурации безопасности должен расширять класс WebSecurityConfigurerAdapter.
package com.tutorialspoint.websecurityapp; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private CustomDetailsService customDetailsService; @Bean public PasswordEncoder encoder() { return new BCryptPasswordEncoder(); } @Override @Autowired protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(customDetailsService).passwordEncoder(encoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().authenticated().and().sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.NEVER); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring(); } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }
Теперь определите класс конфигурации OAuth2, чтобы добавить идентификатор клиента, секрет клиента, определить JwtAccessTokenConverter, закрытый ключ и открытый ключ для ключа подписывающего токена и ключ верификатора, а также сконфигурировать ClientDetailsServiceConfigurer для допустимости токена с областями действия.
package com.tutorialspoint.websecurityapp; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; @Configuration public class OAuth2Config extends AuthorizationServerConfigurerAdapter { private String clientid = "tutorialspoint"; private String clientSecret = "my-secret-key"; private String privateKey = "private key"; private String publicKey = "public key"; @Autowired @Qualifier("authenticationManagerBean") private AuthenticationManager authenticationManager; @Bean public JwtAccessTokenConverter tokenEnhancer() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey(privateKey); converter.setVerifierKey(publicKey); return converter; } @Bean public JwtTokenStore tokenStore() { return new JwtTokenStore(tokenEnhancer()); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore()) .accessTokenConverter(tokenEnhancer()); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()"); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory().withClient(clientid).secret(clientSecret).scopes("read", "write") .authorizedGrantTypes("password", "refresh_token").accessTokenValiditySeconds(20000) .refreshTokenValiditySeconds(20000); } }
Теперь создайте закрытый ключ и открытый ключ с помощью openssl.
Вы можете использовать следующие команды для генерации закрытого ключа.
openssl genrsa -out jwt.pem 2048 openssl rsa -in jwt.pem
Вы можете использовать Для генерации открытого ключа используйте следующие команды.
openssl rsa -in jwt.pem -pubout
Для версии Spring Boot, более поздней, чем 1.5, добавьте свойство ниже в свой файл application.properties, чтобы определить порядок фильтрации OAuth2-ресурсов.
security.oauth2.resource.filter-order=3
Пользователи файла YAML могут добавить свойство ниже в файл YAML.
security: oauth2: resource: filter-order: 3
Теперь создайте файл schema.sql и data.sql в каталоге classpath resources src / main / resources /, чтобы подключить приложение к базе данных H2.
Файл schema.sql выглядит так:
CREATE TABLE USERS (ID INT PRIMARY KEY, USERNAME VARCHAR(45), PASSWORD VARCHAR(60));
Файл data.sql выглядит так:
INSERT INTO USERS (ID, USERNAME,PASSWORD) VALUES ( 1, '[email protected]','$2a$08$fL7u5xcvsZl78su29x1ti.dxI.9rYO8t0q5wk2ROJ.1cdR53bmaVG'); INSERT INTO USERS (ID, USERNAME,PASSWORD) VALUES ( 2, '[email protected]','$2a$08$fL7u5xcvsZl78su29x1ti.dxI.9rYO8t0q5wk2ROJ.1cdR53bmaVG');
Примечание. Пароль должен храниться в формате Bcrypt Encoder в таблице базы данных.
Вы можете создать исполняемый файл JAR и запустить приложение Spring Boot, используя следующие команды Maven или Gradle.
Для Maven, вы можете использовать команду, приведенную ниже —
mvn clean install
После «BUILD SUCCESS» вы можете найти файл JAR в целевом каталоге.
Для Gradle вы можете использовать команду, как показано на рисунке —
gradle clean build
После «BUILD SUCCESSFUL» вы можете найти файл JAR в каталоге build / libs.
Теперь запустите файл JAR с помощью команды, показанной здесь —
java –jar <JARFILE>
Приложение запускается на порт Tomcat 8080.
Теперь нажмите URL-адрес метода POST через POSTMAN, чтобы получить токен OAUTH2.
HTTP: // локальный: 8080 / OAuth / маркер
Теперь добавьте заголовки запроса следующим образом:
-
Авторизация — базовая аутентификация с вашим идентификатором клиента и секретом клиента.
-
Тип контента — application / x-www-form-urlencoded
Авторизация — базовая аутентификация с вашим идентификатором клиента и секретом клиента.
Тип контента — application / x-www-form-urlencoded
Теперь добавьте параметры запроса следующим образом:
- grant_type = пароль
- имя пользователя = ваше имя пользователя
- пароль = ваш пароль
Теперь нажмите API и получите access_token, как показано на рисунке —
Теперь нажмите на Resource Server API с маркером доступа Bearer в заголовке запроса, как показано.
Затем вы можете увидеть результат, как показано ниже —