Spring cloud入门-OAuth 2.0(三)JWT集成

JWT简介

JWT是JSON WEB TOKEN的简写,它是基于 RFC 7519 标准定义的一种可以安全传输的的JSON对象,由于使用了数字签名,所以是可信任和安全的。

JWT组成

JWT组成有三部分

  1. Header
  2. Payload
  3. Signature

Header 中存在签名的生成算法,比如

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

payload中用于存放数据,比如过期时间、用户名、用户所拥有的权限等;

{
  "user_name": "jourwon",
  "scope": [
    "all"
  ],
  "exp": 1577678449,
  "authorities": [
    "admin"
  ],
  "jti": "618cda6a-dfce-4966-b0df-00607f693ab5",
  "client_id": "admin"
}

signature为以header和payload生成的签名,一旦header和payload被篡改,验证将失败。

一个简单的JWT字符

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJqb3Vyd29uIiwic2NvcGUiOlsiYWxsIl0sImV4cCI6MTU3NzY3Nzc2MywiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianRpIjoiMGY4NmE2ODUtZDIzMS00M2E0LWJhZjYtNzAwMmE0Yzg1YmM1IiwiY2xpZW50X2lkIjoiYWRtaW4iLCJlbmhhbmNlIjoiZW5oYW5jZSBpbmZvIn0.RLrkBQEOdCikiz0SsJ8ZsVcxk8GkAyKsOj5fZytgNF8

可以在下面地址进行解析
https://jwt.io/

创建oauth2-jwt-server模块

前面生成的令牌是存放在内存中的,事实上令牌可以存放在redis,jwt

使用jwt 存储

package com.zglx.jwt.config;

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

/**
 * @Description:
 * @Author: Zlx
 * @Date: 2021/10/20 12:23 上午
 */
@Configuration

public class JwtTokenStoreConfig {
	@Bean
	@Primary
	public TokenStore jwtTokenStore() {
		return new JwtTokenStore(this.jwtAccessTokenConverter());
	}


	@Bean
	public JwtAccessTokenConverter jwtAccessTokenConverter() {
		JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
		// 配置jwt使用的密钥
		jwtAccessTokenConverter.setSigningKey("test_key");
		return jwtAccessTokenConverter;
	}
}

package com.zglx.jwt.config;

import com.zglx.jwt.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.token.TokenEnhancer;
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.ArrayList;
import java.util.List;

/**
 * @Description: 授权服务器
 * @Author: Zlx
 * @Date: 2021/10/17 5:09 下午
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
	@Autowired
	private PasswordEncoder passwordEncoder;

	@Autowired
	private AuthenticationManager authenticationManager;

	@Autowired
	private UserService userService;

	@Autowired
	@Qualifier("redisTokenStore")
	private TokenStore tokenStore;

	@Qualifier("jwtTokenStore")
	@Autowired
	private TokenStore jwtTokenStore;

	@Autowired
	@Qualifier("jwtAccessTokenConverter")
	private JwtAccessTokenConverter jwtAccessTokenConverter;

	@Autowired
	private JwtTokenEnhancer jwtTokenEnhancer;


	/**
	 * 使用密码模式需要配置
	 *
	 * @param endpoints endpoints
	 * @throws Exception
	 */
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//		// 自定义JWT内容增强器
		TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
		List<TokenEnhancer> delegates = new ArrayList<>();
		// 配置jwt内容增强器
		delegates.add(jwtTokenEnhancer);
		delegates.add(jwtAccessTokenConverter);
		tokenEnhancerChain.setTokenEnhancers(delegates);
		endpoints.authenticationManager(authenticationManager)
				// 配置用户数据源,登录的时候会执行userService的loadUserByUsername方法
				// 登录地址
				// http://localhost:9401/oauth/authorize?response_type=code&client_id=admin&redirect_uri=http://www.baidu.com&scope=all&state=normal
				.userDetailsService(userService)
				// 设置存储方式,这里使用redis
//				.tokenStore(tokenStore);
				.tokenStore(jwtTokenStore)
				.tokenEnhancer(tokenEnhancerChain)
				// 设置自定义token构造器
				.accessTokenConverter(jwtAccessTokenConverter);
	}

	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		clients.inMemory()
				// 配置client_id
				.withClient("admin")
				// 配置client_secret
				.secret(passwordEncoder.encode("admin"))
				// 配置访问token的有效期
				.accessTokenValiditySeconds(3600)
				// 配置刷新token的有效期
				.refreshTokenValiditySeconds(864000)
				// 配置redirect_uri,用于授权成功后的跳转
				.redirectUris("http://www.baidu.com")
				// 配置申请的权限范围
				.scopes("all")
				// 配置grant_type,表示授权类型
				.authorizedGrantTypes("authorization_code", "password", "refresh_token");
	}
}

运行项目后使用密码模式来获取令牌,访问如下地址:http://localhost:9200/oauth/token

在这里插入图片描述

扩展JWT中存储的内容

有时候我们需要扩展JWT中存储的内容,这里我们在JWT中扩展一个key为info,value为test_info的数据。

我们需要一个TokenEnhancer实现一个JWT内容的增强器

package com.zglx.jwt.config;

import ch.qos.logback.core.rolling.helper.TokenConverter;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;

import java.util.HashMap;
import java.util.Map;

/**
 * @Description: 自定义Token构造器
 * @Author: Zlx
 * @Date: 2021/10/20 12:29 上午
 */
public class JwtTokenEnhancer implements TokenEnhancer {
	@Override
	public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
		Map<String, Object> info = new HashMap<>();
		info.put("enhance", "enhance info");

		((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(info);
		return oAuth2AccessToken;
	}
}

将其注入一个Bean中


	@Bean
	public JwtTokenEnhancer jwtTokenEnhancer() {
		return new JwtTokenEnhancer();
	}

最后再授权服务器中加入自定义Token 构造器


	/**
	 * 使用密码模式需要配置
	 *
	 * @param endpoints endpoints
	 * @throws Exception
	 */
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//		// 自定义JWT内容增强器
		TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
		List<TokenEnhancer> delegates = new ArrayList<>();
		// 配置jwt内容增强器
		delegates.add(jwtTokenEnhancer);
		delegates.add(jwtAccessTokenConverter);
		tokenEnhancerChain.setTokenEnhancers(delegates);
		endpoints.authenticationManager(authenticationManager)
				// 配置用户数据源,登录的时候会执行userService的loadUserByUsername方法
				// 登录地址
				// http://localhost:9401/oauth/authorize?response_type=code&client_id=admin&redirect_uri=http://www.baidu.com&scope=all&state=normal
				.userDetailsService(userService)
				// 设置存储方式,这里使用redis
//				.tokenStore(tokenStore);
				.tokenStore(jwtTokenStore)
				.tokenEnhancer(tokenEnhancerChain)
				// 设置自定义token构造器
				.accessTokenConverter(jwtAccessTokenConverter);
	}

在这里插入图片描述

在这里插入图片描述

解析JWT中的内容

如果我们需要获取JWT中的信息,可以使用一个叫jjwt的工具包。

<dependency>
	<groupId>io.jsonwebtoken</groupId>
	<artifactId>jjwt</artifactId>
	<version>0.9.1</version>
</dependency>
package com.zglx.jwt.controller;

import cn.hutool.core.util.StrUtil;
import io.jsonwebtoken.Jwts;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;

/**
 * @Description:
 * @Author: Zlx
 * @Date: 2021/10/20 12:36 上午
 */
@RestController
@RequestMapping("/user")
public class UserController {
	@GetMapping("/getCurrentUser")
	public Object getCurrentUser(Authentication authentication, HttpServletRequest request) {
		String header = request.getHeader("Authorization");
		String token = StrUtil.subAfter(header, "bearer", false);

		return Jwts.parser()
				.setSigningKey("test_key".getBytes(StandardCharsets.UTF_8))
				.parseClaimsJws(token)
				.getBody();
	}

}

将令牌放入Authorization头中,访问如下地址获取信息:http://localhost:9200/user/getCurrentUser

在这里插入图片描述

刷新令牌

在Spring Cloud Security 中使用oauth2时,如果令牌失效了,可以使用刷新令牌通过refresh_token的授权模式再次获取access_token。

需要给授权服务器增加refresh_token的工作模式


	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		clients.inMemory()
				// 配置client_id
				.withClient("admin")
				// 配置client_secret
				.secret(passwordEncoder.encode("admin"))
				// 配置访问token的有效期
				.accessTokenValiditySeconds(3600)
				// 配置刷新token的有效期
				.refreshTokenValiditySeconds(864000)
				// 配置redirect_uri,用于授权成功后的跳转
				.redirectUris("http://www.baidu.com")
				// 配置申请的权限范围
				.scopes("all")
				// 配置grant_type,表示授权类型,
				.authorizedGrantTypes("authorization_code", "password", "refresh_token");
	}

在这里插入图片描述

使用Redis 存储令牌

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</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.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-security</artifactId>
</dependency>

server:
  port: 9200

spring:
  application:
    name: oauth2-server-jwt
  redis:
    host: localhost
    database: 0
/**
 * @Description: oauth2 可以设置存储令牌的方式,自己实现一个bean注入就行
 * @Author: Zlx
 * @Date: 2021/10/18 10:36 下午
 */
@Configuration
public class RedisTokenStoreConfig {
	@Autowired
	private RedisConnectionFactory redisConnectionFactory;

	@Bean
	public TokenStore redisTokenStore() {
		return new RedisTokenStore(redisConnectionFactory);
	}
}

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserService userService;

    @Autowired
    @Qualifier("redisTokenStore")
    private TokenStore tokenStore;

    /**
     * 使用密码模式需要配置
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userService)
                //配置令牌存储策略
                .tokenStore(tokenStore);
    }
    
    //省略代码...
}


运行项目后使用密码模式来获取令牌,访问如下地址:http://localhost:9200/oauth/token

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值