oauth2.0--基础--04--搭建JWT

oauth2.0–基础–04–搭建JWT


代码文章

https://gitee.com/DanShenGuiZu/learnDemo/tree/master/auth2.0--learn/simple-auth2.0/oauth_parent

1、介绍

  1. 框架:spring sercurity+oauth2.0+JWT
  2. 需要完成下面的内容
    1. oauth2.0–基础–03–简单搭建认证服务器,资源服务器

2、JWT

2.1、JWT 概念

  1. JSON Web Token
  2. 一种简介的、自包含的协议格式
  3. 用于在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。
  4. JWT可以使用HMAC算法或使用RSA的公钥/私钥对来签名,防止被篡改。

2.2、优点

  1. jwt基于json,非常方便解析
  2. 可以在令牌中自定义丰富的内容,易扩展
  3. 通过非对称加密算法及数字签名技术,JWT防止篡改,安全性高
  4. 资源服务使用JWT可不依赖认证服务即可完成授权

2.3、缺点

JWT令牌较长,占存储空间比较大。

2.4、JWT令牌结构

  1. Header:头部
  2. Payload:负载
  3. Signature:签名
  4. 每部分中间使用点(.)分隔,比如:xxxxx.yyyyy.zzzzz
  5. 在线工具 https://jwt.io

2.4.1、Header:头部

包含令牌的类型(即JWT)及使用的哈希算法(如HMACSHA256或RSA)

将以下内容使用Base64Url编码,得到一个字符串就是JWT令牌的Header。


{
  "alg": "HS256",
  "typ": "JWT"
}

2.4.2、Payload:负载

  1. 存放有效信息的地方,它可以存放jwt提供的现成字段,比如:iss(签发者),exp(过期时间戳),sub(面向的用户)等,也可自定义字段。
  2. 此部分不建议存放敏感信息
  3. 此部分可以解码还原原始内容。

将内容使用Base64Url编码,得到一个字符串就是Payload。

{
  "sub": "1234567890",
  "name": "456",
  "admin": true
}

2.4.3、Signature:签名

  1. 用于防止jwt内容被篡改。
  2. 这个部分使用base64url将前两部分进行编码,编码后使用点(.)连接组成字符串,最后使用header中声明签名算法进行签名。
HMACSHA256(
		base64UrlEncode(header)+"."+
		base64UrlEncode(payload),
		secret)
  1. base64UrlEncode(header):jwt令牌的第一部分
  2. base64UrlEncode(payload):jwt令牌的第二部分
  3. secret:签名所使用的密钥

3、搭建spring sercurity+oauth2.0+JWT的框架

3.1、认证服务器:代码

3.1.1、改动点

oauth2.0--基础--04--搭建JWT_第1张图片

oauth2.0--基础--04--搭建JWT_第2张图片

oauth2.0--基础--04--搭建JWT_第3张图片
oauth2.0--基础--04--搭建JWT_第4张图片

3.1.2、代码

AuthorizationServer

package com.feizhou.oauth.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import java.util.Arrays;

/**
 * 描述该类- JPA
 *
 * @author zhoufei
 * @class: AuthorizationServer
 * @date 2020/10/28 21:26
 * @Verson 1.0 -2020/10/28 21:26
 * @see
 */

@Configuration
@EnableAuthorizationServer // 配置授权服务。
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
	
	@Autowired
	private JwtAccessTokenConverter accessTokenConverter;
	@Autowired
	// 授权码服务
	private AuthorizationCodeServices authorizationCodeServices;
	@Autowired
	// 认证管理
	private AuthenticationManager authenticationManager;
	@Autowired
	// 令牌管理服务
	private AuthorizationServerTokenServices authorizationServerTokenServices;
	
	@Autowired
	// 令牌存储策略
	private TokenStore tokenStore;
	@Autowired
	// 客户端详情服务,也就是configure(ClientDetailsServiceConfigurer clients)方法
	private ClientDetailsService clientDetailsService;
	
	// 用来配置客户端详情服务
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		// 这里是第三方合作用户的客户id,秘钥的配置
		// 使用in-memory存储
		clients.inMemory()
				// client_id,用户账号
				.withClient("c1")
				// 客户端密钥
				.secret(new BCryptPasswordEncoder().encode("secret"))
				// 资源列表,资源标识
				.resourceIds("res1")
				// 授权类型(4种)
				.authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit",
						"refresh_token")
				// 客戶端允许的授权范围
				.scopes("all")
				// false跳转到授权页面,让用户点击授权,如果是true,相当于自动点击授权,就不跳转授权页面
				.autoApprove(false)//
				// 加上验证回调地址,返回授权码信息
				.redirectUris("http://www.baidu.com");
		
		// 如果有多个用户,配置多个客户详情
		// .and().withClient()
		
	}
	
	// 令牌管理服务
	@Bean
	public AuthorizationServerTokenServices tokenService() {
		DefaultTokenServices service = new DefaultTokenServices();
		service.setClientDetailsService(clientDetailsService);
		service.setSupportRefreshToken(true);// 支持刷新
		service.setTokenStore(tokenStore);// 令牌存储
		
		// 令牌增强
		TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
		tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
		service.setTokenEnhancer(tokenEnhancerChain);
		
		service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
		service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
		return service;
	}
	
	@Override
	// 用来配置令牌(token)的访问端点
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
		endpoints
				// 指定认证管理器
				.authenticationManager(authenticationManager)
				// 授权码模式需要
				.authorizationCodeServices(authorizationCodeServices)
				// 令牌管理服务
				.tokenServices(authorizationServerTokenServices)
				// jwt格式Token
				.accessTokenConverter(accessTokenConverter)
				// 允许post提交
				.allowedTokenEndpointRequestMethods(HttpMethod.POST);
	}
	
	@Bean
	// 授权码服务器
	public AuthorizationCodeServices authorizationCodeServices() {
		// 授权码模式的授权码采用内存方式存储
		return new InMemoryAuthorizationCodeServices();
	}
	
	@Override
	// 用来配置令牌端点的安全约束,拦截规则
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
		
		security
				// 提供公有密匙的端点,如果你使用JWT令牌的话, 允许
				.tokenKeyAccess("permitAll()")
				// oauth/check_token:用于资源服务访问的令牌解析端点,允许
				.checkTokenAccess("permitAll()")
				// 表单认证,申请令牌
				.allowFormAuthenticationForClients();
	}
	
}

TokenConfig

package com.feizhou.oauth.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;

/**
 * @author Administrator
 * @version 1.0
 **/
@Configuration
public class TokenConfig {
	
	// 对称秘钥,资源服务器使用该秘钥来验证
	private String SIGNING_KEY = "uaa123";
	
	@Bean
	public TokenStore tokenStore() {
		// JWT令牌存储方案
		return new JwtTokenStore(accessTokenConverter());
	}
	
	@Bean
	public JwtAccessTokenConverter accessTokenConverter() {
		JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
		converter.setSigningKey(SIGNING_KEY); // 对称秘钥,资源服务器使用该秘钥来验证
		return converter;
	}

   /* @Bean
	public TokenStore tokenStore() {
		//使用内存存储令牌(普通令牌)
		return new InMemoryTokenStore();
	}*/
}


3.1.3、测试

oauth2.0--基础--04--搭建JWT_第5张图片

3.2、资源服务器:代码

3.2.1、改动点

oauth2.0--基础--04--搭建JWT_第6张图片
oauth2.0--基础--04--搭建JWT_第7张图片
oauth2.0--基础--04--搭建JWT_第8张图片

3.2.2、代码

ResouceServerConfig

package com.feizhou.oauth.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;

/**
 * @author Administrator
 * @version 1.0
 **/
@Configuration
@EnableResourceServer
public class ResouceServerConfig extends ResourceServerConfigurerAdapter {

	@Autowired
	// 令牌存储策略
	private TokenStore tokenStore;


	// 授权服务的资源列表
	public static final String RESOURCE_ID = "res1";
	
	@Override
	public void configure(ResourceServerSecurityConfigurer resources) {
		resources
				// 资源 id
				.resourceId(RESOURCE_ID)
//                // 令牌服务
//                .tokenServices(tokenService())
				// 令牌服务
				.tokenStore(tokenStore)
				.stateless(true);
	}
	
	@Override
	public void configure(HttpSecurity http) throws Exception {
		
		http
				
				.authorizeRequests().antMatchers("/**")
				// 所有的访问,授权访问都要是all,和认证服务器的授权范围一一对应
				.access("#oauth2.hasScope('all')")
				//去掉防跨域攻击
				.and().csrf().disable()
				//session管理
				.sessionManagement()
				.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
	}
	
//    @Bean
//    // 资源服务令牌解析服务
//    public ResourceServerTokenServices tokenService() {
//        // 使用远程服务请求授权服务器校验token,必须指定校验token 的url、client_id,client_secret
//        RemoteTokenServices service = new RemoteTokenServices();
//        service.setCheckTokenEndpointUrl("http://localhost:8081/oauth/check_token");
//        service.setClientId("c1");
//        service.setClientSecret("secret");
//        return service;
//    }
	
}

TokenConfig


package com.feizhou.oauth.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;

/**
 * @author Administrator
 * @version 1.0
 **/
@Configuration
public class TokenConfig {
	
	// 对称秘钥,资源服务器使用该秘钥来验证
	private String SIGNING_KEY = "uaa123";
	
	@Bean
	public TokenStore tokenStore() {
		// JWT令牌存储方案
		return new JwtTokenStore(accessTokenConverter());
	}
	
	@Bean
	public JwtAccessTokenConverter accessTokenConverter() {
		JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
		converter.setSigningKey(SIGNING_KEY); // 对称秘钥,资源服务器使用该秘钥来验证
		return converter;
	}

   /* @Bean
	public TokenStore tokenStore() {
		//使用内存存储令牌(普通令牌)
		return new InMemoryTokenStore();
	}*/
}

3.2.3、测试结果

oauth2.0--基础--04--搭建JWT_第9张图片

4、升级1–客户端信息和授权码持久化到数据库

4.1、改动点如下

oauth2.0--基础--04--搭建JWT_第10张图片
oauth2.0--基础--04--搭建JWT_第11张图片

oauth2.0--基础--04--搭建JWT_第12张图片

4.2、代码

AuthorizationServer


package com.feizhou.oauth.config;

import java.util.Arrays;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

/**
 * 描述该类- JPA
 *
 * @author zhoufei
 * @class: AuthorizationServer
 * @date 2020/10/28 21:26
 * @Verson 1.0 -2020/10/28 21:26
 * @see
 */

@Configuration
@EnableAuthorizationServer // 配置授权服务。
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
	
	@Autowired
	private JwtAccessTokenConverter accessTokenConverter;
	@Autowired
	// 授权码服务
	private AuthorizationCodeServices authorizationCodeServices;
	@Autowired
	// 认证管理
	private AuthenticationManager authenticationManager;
	@Autowired
	// 令牌管理服务
	private AuthorizationServerTokenServices authorizationServerTokenServices;
	
	@Autowired
	// 令牌存储策略
	private TokenStore tokenStore;
	@Autowired
	// 客户端详情服务,也就是configure(ClientDetailsServiceConfigurer clients)方法
	private ClientDetailsService clientDetailsService;

	@Autowired
	// 加密方式
	private PasswordEncoder passwordEncoder;

	//将客户端信息存储到数据库
	@Bean
	public ClientDetailsService clientDetailsService(DataSource dataSource) {
		ClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
		((JdbcClientDetailsService) clientDetailsService).setPasswordEncoder(passwordEncoder);
		return clientDetailsService;
	}


	// 用来配置客户端详情服务
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		//数据库配置
		clients.withClientDetails(clientDetailsService);
	}


//    // 用来配置客户端详情服务
//    @Override
//    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//        // 这里是第三方合作用户的客户id,秘钥的配置
//        // 使用in-memory存储
//        clients.inMemory()
//                // client_id,用户账号
//                .withClient("c1")
//                // 客户端密钥
//                .secret(new BCryptPasswordEncoder().encode("secret"))
//                // 资源列表,资源标识
//                .resourceIds("res1")
//                // 授权类型(4种)
//                .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit",
//                        "refresh_token")
//                // 客戶端允许的授权范围
//                .scopes("all")
//                // false跳转到授权页面,让用户点击授权,如果是true,相当于自动点击授权,就不跳转授权页面
//                .autoApprove(false)//
//                // 加上验证回调地址,返回授权码信息
//                .redirectUris("http://www.baidu.com");
//
//        // 如果有多个用户,配置多个客户详情
//        // .and().withClient()
//
//    }


	// 令牌管理服务
	@Bean
	public AuthorizationServerTokenServices tokenService() {
		DefaultTokenServices service = new DefaultTokenServices();
		service.setClientDetailsService(clientDetailsService);
		service.setSupportRefreshToken(true);// 支持刷新
		service.setTokenStore(tokenStore);// 令牌存储
		
		// 令牌增强
		TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
		tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
		service.setTokenEnhancer(tokenEnhancerChain);
		
		service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
		service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
		return service;
	}
	
	@Override
	// 用来配置令牌(token)的访问端点
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
		endpoints
				// 指定认证管理器
				.authenticationManager(authenticationManager)
				// 授权码模式需要
				.authorizationCodeServices(authorizationCodeServices)
				// 令牌管理服务
				.tokenServices(authorizationServerTokenServices)
				// jwt格式Token
				.accessTokenConverter(accessTokenConverter)
				// 允许post提交
				.allowedTokenEndpointRequestMethods(HttpMethod.POST);
	}
	
//    @Bean
//    // 授权码服务器
//    public AuthorizationCodeServices authorizationCodeServices() {
//        // 授权码模式的授权码采用内存方式存储
//        return new InMemoryAuthorizationCodeServices();
//    }

	@Bean
	// 授权码服务器
	public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource) {
		//授权码模式的授权码采用数据库方式存储
		return new JdbcAuthorizationCodeServices(dataSource);
	}
	
	@Override
	// 用来配置令牌端点的安全约束,拦截规则
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
		
		security
				// 提供公有密匙的端点,如果你使用JWT令牌的话, 允许
				.tokenKeyAccess("permitAll()")
				// oauth/check_token:用于资源服务访问的令牌解析端点,允许
				.checkTokenAccess("permitAll()")
				// 表单认证,申请令牌
				.allowFormAuthenticationForClients();
	}
	
}

application.properties


spring.application.name=oauth-authorizationServer
server.port=8081

spring.datasource.url = jdbc:mysql://zhoufei.ali.db.com:3306/test?useUnicode=true
spring.datasource.username = root
spring.datasource.password = root
spring.datasource.driver-class-name = com.mysql.jdbc.Driver

spring.main.allow-bean-definition-overriding=true

数据库脚本

CREATE TABLE oauth_client_details (
  client_id varchar(255) NOT NULL COMMENT '客户端标识',
  resource_ids varchar(255) DEFAULT NULL COMMENT '接入资源列表',
  client_secret varchar(255) DEFAULT NULL COMMENT '客户端秘钥',
  scope varchar(255) DEFAULT NULL COMMENT '授权访问',
  authorized_grant_types varchar(255) DEFAULT NULL COMMENT '授权类型',
  web_server_redirect_uri varchar(255) DEFAULT NULL COMMENT '重定向地址',
  authorities varchar(255) DEFAULT NULL COMMENT '客户端秘钥',
  access_token_validity int(11) DEFAULT NULL COMMENT 'token过期时间',
  refresh_token_validity int(11) DEFAULT NULL COMMENT '刷新 token',
  additional_information longtext,
  archived tinyint(4) DEFAULT NULL,
  trusted tinyint(4) DEFAULT NULL,
  autoapprove varchar(255) DEFAULT NULL,
  create_time timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (client_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户端信息表';



CREATE TABLE oauth_code (
  code varchar(255) DEFAULT NULL COMMENT '授权码',
  authentication blob,
  create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='授权码表';



INSERT INTO test.oauth_client_details (client_id, resource_ids, client_secret, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, archived, trusted, autoapprove, create_time) VALUES ('c1', 'res1', '$2a$10$dIONDbZ6k.CHLd6eCJZQS.52LkcutcjJI1CgnsLOl9RNzaPNMaRTS', 'all,ROLE_USER,ROLE_API', 'client_credentials,password,authorization_code,implicit,refresh_token', 'http://www.baidu.com', NULL, '7500', '259200', NULL, '0', '0', 'false', '2020-10-31 09:30:30');

INSERT INTO test.oauth_client_details (client_id, resource_ids, client_secret, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, archived, trusted, autoapprove, create_time) VALUES ('c2', 'res2', '$2a$10$dIONDbZ6k.CHLd6eCJZQS.52LkcutcjJI1CgnsLOl9RNzaPNMaRTS', 'ROLE_API', 'client_credentials,password,authorization_code,implicit,refresh_token', 'http://www.baidu.com', NULL, '31536000', '2592000', NULL, '0', '0', 'false', '2020-10-31 09:27:15');

4.3、测试

4.3.1、获取授权码

oauth2.0--基础--04--搭建JWT_第13张图片

oauth2.0--基础--04--搭建JWT_第14张图片

4.3.2、获取taken

oauth2.0--基础--04--搭建JWT_第15张图片

你可能感兴趣的:(OAuth2,java)