OAuth2是一种授权方法,用于通过HTTP协议提供对受保护资源的访问。首先,OAuth2使第三方应用程序能够获得对HTTP服务的有限访问权限,然后通过资源所有者和HTTP服务之间的批准交互来让第三方应用程序代表资源所有者获取访问权限。
OAuth包括以下角色
- 资源拥有者 - 应用程序的用户。
- 客户端 - ,需要通过资源拥有者的授权去请求资源服务器的资源的应用程序,如Android客户端、Web客户端(浏 览器端)、微信客户端等
- 资源服务器 - 存储用户数据和http服务,可以将用户数据返回给经过身份验证的客户端。
- 授权服务器 - 负责验证用户的身份并提供授权令牌。资源服务器接受此令牌并验证您的身份。
oauth2认证流程
网络异常,图片无法展示
|
创建oauth_client_details表
-- ---------------------------- -- Table structure for oauth_client_details -- ---------------------------- DROP TABLE IF EXISTS `oauth_client_details`; CREATE TABLE `oauth_client_details` ( `client_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '客户端标 识', `resource_ids` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '接入资源列表', `client_secret` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '客户端秘钥', `scope` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `authorized_grant_types` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `web_server_redirect_uri` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `authorities` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `access_token_validity` int DEFAULT NULL, `refresh_token_validity` int DEFAULT NULL, `additional_information` longtext CHARACTER SET utf8 COLLATE utf8_general_ci, `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `archived` tinyint DEFAULT NULL, `trusted` tinyint DEFAULT NULL, `autoapprove` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, PRIMARY KEY (`client_id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC COMMENT='接入客户端信息';
创建oauth_code表
-- ---------------------------- -- Table structure for oauth_code -- ---------------------------- DROP TABLE IF EXISTS `oauth_code`; CREATE TABLE `oauth_code` ( `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `authentication` blob, KEY `code_index` (`code`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=COMPACT;
工程目录
网络异常,图片无法展示
|
创建一个父maven工程,并引入依赖
<?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>tjw</groupId> <artifactId>cloud-oauth2</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <modules> <module>auth-server</module> <module>order-a</module> </modules> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR1</spring-cloud.version> </properties> <dependencies> <!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity5 --> <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity5</artifactId> <version>3.0.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
创建授权服务器
application.yml
server: port: 8081 servlet: context-path: /auth spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3305/item?serverTimezone=UTC username: root password: 123456 main: #当遇到同样名字的bean时,是否允许覆盖注册 allow-bean-definition-overriding: true
授权服务配置类
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired ClientDetailsService clientDetailsService; @Autowired AuthenticationManager authenticationManager; @Autowired AuthorizationCodeServices authorizationCodeServices; @Autowired TokenStore tokenStore; @Autowired JwtAccessTokenConverter jwtAccessTokenConverter; @Autowired PasswordEncoder passwordEncoder; // @Bean // public AuthorizationCodeServices authorizationCodeServices() { // return new InMemoryAuthorizationCodeServices(); // } //基于数据库的授权码服务 @Bean public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource) { return new JdbcAuthorizationCodeServices(dataSource); } //localhost:8081/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com // 令牌管理服务 @Bean public AuthorizationServerTokenServices tokenService() { DefaultTokenServices service=new DefaultTokenServices(); service.setClientDetailsService(clientDetailsService); service.setSupportRefreshToken(true); service.setTokenStore(tokenStore); // jwt令牌配置 TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter)); service.setTokenEnhancer(tokenEnhancerChain); service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时 service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天 return service; return service; } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { super.configure(security); security.tokenKeyAccess("permitAll()") .checkTokenAccess("permitAll()") .allowFormAuthenticationForClients(); } // 重写ClientDetailsService,将客户端的信息存储到数据库 @Bean public ClientDetailsService clientDetailsService(DataSource dataSource){ JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource); jdbcClientDetailsService.setPasswordEncoder(passwordEncoder); return jdbcClientDetailsService; } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.withClientDetails(clientDetailsService); //基于内存 // clients.inMemory() // .withClient("c1")//client_id // .secret(new BCryptPasswordEncoder().encode("secret")) // .resourceIds("res1") // .authorizedGrantTypes("authorization_code","password","client_credentials","implicit","refresh_token")//该client允许的授权类型authorization_code,password,refresh_token,implicit,client_credentials // .scopes("all")//允许的授权范围 // .autoApprove(false) // //加上验证回调地址 // .redirectUris("http://www.baidu.com"); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager) .authorizationCodeServices(authorizationCodeServices) .tokenServices(tokenService()) .allowedTokenEndpointRequestMethods(HttpMethod.POST); } }
属性
clientId - (必需)客户端ID。
secret - (可信客户端所需)客户端密钥(可选)。
scope - 客户受限的范围。如果范围未定义或为空(默认值),则客户端不受范围限制。
authorizedGrantTypes - 授权客户端使用的授权类型。默认值为空。
authorities - 授予客户的权限(常规Spring Security权限)。
redirectUris - 将用户代理重定向到客户端的重定向端点。它必须是绝对URL。
创建资源服务器
@Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true) public class ResourceServerConfig extends ResourceServerConfigurerAdapter { public static final String RESOURCE_ID = "res1"; @Autowired TokenStore tokenStore; public ResourceServerTokenServices tokenServices(){ RemoteTokenServices services = new RemoteTokenServices(); services.setCheckTokenEndpointUrl("http://localhost:8081/auth/oauth/check_token"); services.setClientId("c1"); services.setClientSecret("secret"); return services; } @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.resourceId(RESOURCE_ID) // 使用jwt令牌进行校验 .tokenStore(tokenStore) //验证令牌服务 // .tokenServices(tokenServices()) .stateless(true); } @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/**") // .hasAnyAuthority("all","ROLE_ADMIN") .access("#oauth2.hasScope('ROLE_ADMIN')") .and().csrf().disable() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); } }
资源
@RestController public class OrderController { @GetMapping("/r1") @PreAuthorize("hasAnyAuthority('p1')") public String r1(){ return "资源1"; } }
测试
浏览器输入
localhost:8081/auth/oauth/authorize?client_id=c1&response_type=code&scope=ROLE_ADMIN&redirect_uri=http://www.baidu.com
注意这里的client_id、scope、redirect_uri 的值来自数据库
过浏览器访问上面的URL地址,它将展现一个登录页面。提供用户名和密码。
网络异常,图片无法展示
|
网络异常,图片无法展示
|
点击authorize
网络异常,图片无法展示
|
这样就拿到了授权码,使用授权码来生成JWT token
网络异常,图片无法展示
|
网络异常,图片无法展示
|
使用token,来访问资源
网络异常,图片无法展示
|