oAuth2+JWT+Security实现权限认证体系(上)

简介: oAuth2+JWT+Security实现权限认证体系

前言

今天看一个群里的小伙伴在聊权限认证的有关事情。那么,也来聊一下,采用oAuth2+JWT+Security来实现权限认证的方式方法。

对于这个项目,我们将通过Spring Boot使用Spring Security 5来构建。

开发环境

  • JDK 1.8
  • Maven 3.0+

OAuth2 术语

  • 资源所有者授权应用程序访问其帐户的用户。该访问仅限于范围。
  • 资源服务器:在客户端获得访问令牌后处理已认证请求的服务器,即完成具体业务实现的服务器
  • 客户端代表资源所有者访问受保护资源的应用程序。
  • 授权服务器在成功验证客户端和资源所有者并授权请求后发出访问令牌的服务器。
  • 访问令牌用于访问受保护资源的唯一令牌
  • 范围许可
  • JWTJSON Web Token是一种用于在RFC 7519中定义的在双方之间安全地表示声明的方法
  • 授权类型是一种获取访问令牌的方法

依赖

<dependencies>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-security</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springframework.security.oauth.boot</groupId>
           <artifactId>spring-security-oauth2-autoconfigure</artifactId>
           <version>2.1.2.RELEASE</version>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-jdbc</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-configuration-processor</artifactId>
           <optional>true</optional>
       </dependency>
       <dependency>
           <groupId>com.h2database</groupId>
           <artifactId>h2</artifactId>
           <scope>runtime</scope>
       </dependency>    
</dependencies>

数据库使用

为了方便演示实例,我们采用h2 database数据库

完成基础数据库创建,当然现在流行的是缓存+持久化形式实现。

CREATE TABLE IF NOT EXISTS oauth_client_details (
client_id VARCHAR(256) PRIMARY KEY,
resource_ids VARCHAR(256),
client_secret VARCHAR(256) NOT NULL,
scope VARCHAR(256),
authorized_grant_types VARCHAR(256),
web_server_redirect_uri VARCHAR(256),
authorities VARCHAR(256),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information VARCHAR(4000),
autoapprove VARCHAR(256)
);
CREATE TABLE IF NOT EXISTS oauth_client_token (
token_id VARCHAR(256),
token BLOB,
authentication_id VARCHAR(256) PRIMARY KEY,
user_name VARCHAR(256),
client_id VARCHAR(256)
);
CREATE TABLE IF NOT EXISTS oauth_access_token (
token_id VARCHAR(256),
token BLOB,
authentication_id VARCHAR(256),
user_name VARCHAR(256),
client_id VARCHAR(256),
authentication BLOB,
refresh_token VARCHAR(256)
);
CREATE TABLE IF NOT EXISTS oauth_refresh_token (
token_id VARCHAR(256),
token BLOB,
authentication BLOB
);
CREATE TABLE IF NOT EXISTS oauth_code (
 code VARCHAR(256), authentication BLOB
);

插入示例语句

-- 客户端信息
INSERT INTO oauth_client_details (client_id, client_secret, scope, authorized_grant_types, authorities, access_token_validity)
 VALUES ('clientId', '{bcrypt}$2a$10$vCXMWCn7fDZWOcLnIEhmK.74dvK1Eh8ae2WrWlhr2ETPLoxQctN4.', 'read,write', 'password,refresh_token,client_credentials', 'ROLE_CLIENT', 300);

含义是,客户端clientID具有读写许可,授权类型为密码、刷新令牌、客户端认证 有效期300ms

完善,建表语句

CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(256) NOT NULL,
 password VARCHAR(256) NOT NULL,
enabled TINYINT(1),
 UNIQUE KEY unique_username(username)
);
CREATE TABLE IF NOT EXISTS authorities (
username VARCHAR(256) NOT NULL,
authority VARCHAR(256) NOT NULL,
 PRIMARY KEY(username, authority)
);

插入基础数据

-- 加密的密码是 `pass`
INSERT INTO users (id, username, password, enabled) VALUES (1, 'user', '{bcrypt}$2a$10$cyf5NfobcruKQ8XGjUJkEegr9ZWFqaea6vjpXWEaSqTa2xL9wjgQC', 1);
INSERT INTO authorities (username, authority) VALUES ('user', 'ROLE_USER');

Spring Security配置

import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.sql.DataSource;
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
   private final DataSource dataSource;
   private PasswordEncoder passwordEncoder;
   private UserDetailsService userDetailsService;
   public WebSecurityConfiguration(final DataSource dataSource) {
       this.dataSource = dataSource;
  }
   @Override
   protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
       auth.userDetailsService(userDetailsService())
              .passwordEncoder(passwordEncoder());
  }
   @Bean
   @Override
   public AuthenticationManager authenticationManagerBean() throws Exception {
       return super.authenticationManagerBean();
  }
   @Bean
   public PasswordEncoder passwordEncoder() {
       if (passwordEncoder == null) {
           passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
      }
       return passwordEncoder;
  }
   @Bean
   public UserDetailsService userDetailsService() {
       if (userDetailsService == null) {
           userDetailsService = new JdbcDaoImpl();
          ((JdbcDaoImpl) userDetailsService).setDataSource(dataSource);
      }
       return userDetailsService;
  }
}

使用的是Spring Boot,则将自动配置DataSource对象

由于某些自动配置的Spring @Bean需要Spring Security的AuthenticationManager,因此有必要重写authenticationManagerBean方法并将其注释为@Bean。

PasswordEncoder将由PasswordEncoderFactories.createDelegatingPasswordEncoder()处理,该处理基于前缀的一些密码编码器和委托,在我们的示例中,我们使用{bcrypt}作为密码的前缀。

授权服务器配置

授权服务器会验证客户端和用户凭据并提供令牌,在本示例中,我们将生成JSON Web Token又名JWT。

要对生成的JWT令牌进行签名,我们将使用自签名证书,并在使用Spring Configuration开始之前这样做,创建一个@ConfigurationProperties类来绑定我们的配置属性。

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.io.Resource;
@ConfigurationProperties("security")
public class SecurityProperties {
   private JwtProperties jwt;
   public JwtProperties getJwt() {
       return jwt;
  }
   public void setJwt(JwtProperties jwt) {
       this.jwt = jwt;
  }
   public static class JwtProperties {
       private Resource keyStore;
       private String keyStorePassword;
       private String keyPairAlias;
       private String keyPairPassword;
       public Resource getKeyStore() {
           return keyStore;
      }
       public void setKeyStore(Resource keyStore) {
           this.keyStore = keyStore;
      }
       public String getKeyStorePassword() {
           return keyStorePassword;
      }
       public void setKeyStorePassword(String keyStorePassword) {
           this.keyStorePassword = keyStorePassword;
      }
       public String getKeyPairAlias() {
           return keyPairAlias;
      }
       public void setKeyPairAlias(String keyPairAlias) {
           this.keyPairAlias = keyPairAlias;
      }
       public String getKeyPairPassword() {
           return keyPairPassword;
      }
       public void setKeyPairPassword(String keyPairPassword) {
           this.keyPairPassword = keyPairPassword;
      }
  }
}

添加配置类

import com.marcosbarbero.lab.sec.oauth.jwt.config.props.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
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.configuration.EnableAuthorizationServer;
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.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import javax.sql.DataSource;
import java.security.KeyPair;
@Configuration
@EnableAuthorizationServer
@EnableConfigurationProperties(SecurityProperties.class)
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
   private final DataSource dataSource;
   private final PasswordEncoder passwordEncoder;
   private final AuthenticationManager authenticationManager;
   private final SecurityProperties securityProperties;
   private JwtAccessTokenConverter jwtAccessTokenConverter;
   private TokenStore tokenStore;
   public AuthorizationServerConfiguration(final DataSource dataSource, final PasswordEncoder passwordEncoder,
                                           final AuthenticationManager authenticationManager, final SecurityProperties securityProperties) {
       this.dataSource = dataSource;
       this.passwordEncoder = passwordEncoder;
       this.authenticationManager = authenticationManager;
       this.securityProperties = securityProperties;
  }
   @Bean
   public TokenStore tokenStore() {
       if (tokenStore == null) {
           tokenStore = new JwtTokenStore(jwtAccessTokenConverter());
      }
       return tokenStore;
  }
   @Bean
   public DefaultTokenServices tokenServices(final TokenStore tokenStore,
                                             final ClientDetailsService clientDetailsService) {
       DefaultTokenServices tokenServices = new DefaultTokenServices();
       tokenServices.setSupportRefreshToken(true);
       tokenServices.setTokenStore(tokenStore);
       tokenServices.setClientDetailsService(clientDetailsService);
       tokenServices.setAuthenticationManager(this.authenticationManager);
       return tokenServices;
  }
   @Bean
   public JwtAccessTokenConverter jwtAccessTokenConverter() {
       if (jwtAccessTokenConverter != null) {
           return jwtAccessTokenConverter;
      }
       SecurityProperties.JwtProperties jwtProperties = securityProperties.getJwt();
       KeyPair keyPair = keyPair(jwtProperties, keyStoreKeyFactory(jwtProperties));
       jwtAccessTokenConverter = new JwtAccessTokenConverter();
       jwtAccessTokenConverter.setKeyPair(keyPair);
       return jwtAccessTokenConverter;
  }
   @Override
   public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
       clients.jdbc(this.dataSource);
  }
   @Override
   public void configure(final AuthorizationServerEndpointsConfigurer endpoints) {
       endpoints.authenticationManager(this.authenticationManager)
              .accessTokenConverter(jwtAccessTokenConverter())
              .tokenStore(tokenStore());
  }
   @Override
   public void configure(final AuthorizationServerSecurityConfigurer oauthServer) {
       oauthServer.passwordEncoder(this.passwordEncoder).tokenKeyAccess("permitAll()")
              .checkTokenAccess("isAuthenticated()");
  }
   private KeyPair keyPair(SecurityProperties.JwtProperties jwtProperties, KeyStoreKeyFactory keyStoreKeyFactory) {
       return keyStoreKeyFactory.getKeyPair(jwtProperties.getKeyPairAlias(), jwtProperties.getKeyPairPassword().toCharArray());
  }
   private KeyStoreKeyFactory keyStoreKeyFactory(SecurityProperties.JwtProperties jwtProperties) {
       return new KeyStoreKeyFactory(jwtProperties.getKeyStore(), jwtProperties.getKeyStorePassword().toCharArray());
  }
}
目录
相关文章
|
2月前
|
SQL Java 测试技术
在Spring boot中 使用JWT和过滤器实现登录认证
在Spring boot中 使用JWT和过滤器实现登录认证
131 0
|
2月前
|
存储 JSON 安全
从入门到精通:Python中的OAuth与JWT,打造无懈可击的认证体系🔒
【8月更文挑战第4天】构建现代Web和移动应用时,用户认证与授权至关重要。Python集成OAuth和JWT技术,能轻松实现安全认证。本文从OAuth基础入手,介绍如何使用`requests-oauthlib`库简化流程,再到JWT进阶应用,利用`PyJWT`库生成及验证令牌。最后,探讨如何结合两者,创建无缝认证体验。通过代码示例,由浅入深地引导读者掌握构建坚固应用认证体系的方法。
62 2
|
21天前
|
安全 Java 数据安全/隐私保护
|
1月前
|
JSON 安全 数据安全/隐私保护
Python 安全性大揭秘:OAuth 与 JWT,不只是认证,更是信任的传递
【9月更文挑战第4天】在数字化时代,确保应用安全至关重要。Python 作为广泛使用的编程语言,提供了强大的安全认证工具,如 OAuth 和 JWT。OAuth 是一种授权框架,允许第三方应用在有限权限下访问用户资源;JWT 则是一种自包含的数据传输格式,用于安全地传递声明。通过合理配置和使用这些技术,可以有效提升应用安全性,保障用户数据安全。正确管理和定期更新密钥、严格测试 JWT 的生成与验证等最佳实践,对于构建安全可靠的应用至关重要。不断学习新威胁,是维护应用安全的永恒课题。
39 2
|
2月前
|
JSON 安全 数据安全/隐私保护
Python 安全性大揭秘:OAuth 与 JWT,不只是认证,更是信任的传递
【8月更文挑战第6天】在数字化时代,Python 通过 OAuth 和 JWT 筑牢应用安全防线。OAuth 是一种授权框架,允许第三方应用在用户授权下安全访问资源;JWT 则是一种自包含的声明传输格式,确保通信安全。两者结合使用,能有效进行身份验证及授权管理。然而,密钥管理和 JWT 有效期设置等仍是挑战,需谨慎处理以保障整体安全性。正确配置这些工具和技术,可为用户提供既安全又便捷的服务体验。
36 7
|
2月前
|
JSON 安全 数据安全/隐私保护
Python安全新篇章:OAuth与JWT携手,开启认证与授权的新时代
【8月更文挑战第6天】随着互联网应用的发展,安全认证与授权变得至关重要。本文介绍OAuth与JWT两种关键技术,并展示如何结合它们构建安全系统。OAuth允许用户授权第三方应用访问特定信息,无需分享登录凭证。JWT是一种自包含的信息传输格式,用于安全地传递信息。通过OAuth认证用户并获取JWT,可以验证用户身份并保护数据安全,为用户提供可靠的身份验证体验。
36 6
|
2月前
|
JSON 人工智能 算法
Golang 搭建 WebSocket 应用(四) - jwt 认证
Golang 搭建 WebSocket 应用(四) - jwt 认证
42 0
|
2月前
|
JSON 安全 数据安全/隐私保护
Python认证新风尚:OAuth遇上JWT,安全界的时尚Icon👗
【8月更文挑战第5天】在日益重视数据安全与隐私保护的互联网时代,Python 作为一种流行的 Web 开发语言,其认证机制不断演进。OAuth 2.0 与 JSON Web Tokens (JWT) 成为两大热门安全认证方案,既能保障数据安全传输,又能简化用户认证流程。OAuth 2.0 通过角色分配(如资源拥有者、客户端等)实现授权,而 JWT 则以自我包含式令牌简化认证过程。在 Python 中,可以利用 Flask-OAuthlib 和 PyJWT 等库轻松实现这两种认证方式。
24 0
|
3月前
|
存储 监控 安全
通用快照方案问题之JWT认证如何解决
通用快照方案问题之JWT认证如何解决
24 0
|
5月前
|
安全 数据安全/隐私保护
Springboot+Spring security +jwt认证+动态授权
Springboot+Spring security +jwt认证+动态授权
188 0

热门文章

最新文章

下一篇
无影云桌面