@

OAuth2.0系列博客:

1、文章前言介绍

前面文章中我们学习了OAuth2的一些基本概念,对OAuth2有了基本的认识,也对OAuth2.0的令牌等进行数据库存储,对应博客:jdbc方式的数据存储,然后如果不想存储令牌可以实现?

IDEA中,Ctrl+Alt+B,可以看到TokenStore的实现,有如下几种:



ok,其实对于token存储有如上方式,分别进行介绍:

  • InMemoryTokenStore,默认存储,保存在内存
  • JdbcTokenStore,access_token存储在数据库
  • JwtTokenStore,JWT这种方式比较特殊,这是一种无状态方式的存储,不进行内存、数据库存储,只是JWT中携带全面的用户信息,保存在jwt中携带过去校验就可以
  • RedisTokenStore,将 access_token 存到 redis 中。
  • JwkTokenStore,将 access_token 保存到 JSON Web Key。

2、例子实验验证

实验环境准备:

  • IntelliJ IDEA
  • Maven3.+版本

    新建SpringBoot Initializer项目,可以命名oauth2-jwt



主要加入如下配置:

 <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Cloud Oauth2-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<!-- Spring Cloud Security-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>

TokenStore:

   @Bean
public TokenStore jwtTokenStore() {
//基于jwt实现令牌(Access Token)
return new JwtTokenStore(accessTokenConverter());
}

JwtAccessTokenConverter :

 @Bean
public JwtAccessTokenConverter accessTokenConverter(){
JwtAccessTokenConverter converter = new JwtAccessTokenConverter(){
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
String grantType = authentication.getOAuth2Request().getGrantType();
//授权码和密码模式才自定义token信息
if(AUTHORIZATION_CODE.equals(grantType) || GRANT_TYPE_PASSWORD.equals(grantType)) {
String userName = authentication.getUserAuthentication().getName();
// 自定义一些token 信息
Map<String, Object> additionalInformation = new HashMap<String, Object>(16);
additionalInformation.put("user_name", userName);
additionalInformation = Collections.unmodifiableMap(additionalInformation);
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);
}
OAuth2AccessToken token = super.enhance(accessToken, authentication);
return token;
}
};
// 设置签署key
converter.setSigningKey("signingKey");
return converter;
}

配置accessTokenConverter

 @Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(jwtTokenStore()).authenticationManager(authenticationManager)
//自定义accessTokenConverter
.accessTokenConverter(accessTokenConverter())
//支持获取token方式
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST,HttpMethod.PUT,HttpMethod.DELETE,HttpMethod.OPTIONS);
}

总的配置类参考:

package com.example.springboot.oauth2.configuration;

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.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
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.OAuth2Authentication;
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 java.util.Collections;
import java.util.HashMap;
import java.util.Map; /**
* <pre>
* OAuth2.0配置类
* </pre>
*
* <pre>
* @author mazq
* 修改记录
* 修改后版本: 修改人: 修改日期: 2020/06/17 11:44 修改内容:
* </pre>
*/
@Configuration
@EnableAuthorizationServer
public class OAuth2Configuration extends AuthorizationServerConfigurerAdapter { private static final String CLIENT_ID = "cms";
private static final String SECRET_CHAR_SEQUENCE = "{noop}secret";
private static final String SCOPE_READ = "read";
private static final String SCOPE_WRITE = "write";
private static final String TRUST = "trust";
private static final String USER ="user";
private static final String ALL = "all";
private static final int ACCESS_TOKEN_VALIDITY_SECONDS = 2*60;
private static final int FREFRESH_TOKEN_VALIDITY_SECONDS = 2*60;
// 密码模式授权模式
private static final String GRANT_TYPE_PASSWORD = "password";
//授权码模式
private static final String AUTHORIZATION_CODE = "authorization_code";
//refresh token模式
private static final String REFRESH_TOKEN = "refresh_token";
//简化授权模式
private static final String IMPLICIT = "implicit";
//指定哪些资源是需要授权验证的
private static final String RESOURCE_ID = "resource_id"; @Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager; @Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
// 使用内存存储
.inMemory()
//标记客户端id
.withClient(CLIENT_ID)
//客户端安全码
.secret(SECRET_CHAR_SEQUENCE)
//为true 直接自动授权成功返回code
.autoApprove(true)
.redirectUris("http://127.0.0.1:8084/cms/login") //重定向uri
//允许授权范围
.scopes(ALL)
//token 时间秒
.accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
//刷新token 时间 秒
.refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS)
//允许授权类型
.authorizedGrantTypes(GRANT_TYPE_PASSWORD , AUTHORIZATION_CODE , REFRESH_TOKEN , IMPLICIT);
} @Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(jwtTokenStore()).authenticationManager(authenticationManager)
//自定义accessTokenConverter
.accessTokenConverter(accessTokenConverter())
//支持获取token方式
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST,HttpMethod.PUT,HttpMethod.DELETE,HttpMethod.OPTIONS);
} /**
* 认证服务器的安全配置
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
// 开启/oauth/token_key验证端口认证权限访问
.tokenKeyAccess("isAuthenticated()")
// 开启/oauth/check_token验证端口认证权限访问
.checkTokenAccess("isAuthenticated()")
//允许表单认证
.allowFormAuthenticationForClients();
} @Bean
public JwtAccessTokenConverter accessTokenConverter(){
JwtAccessTokenConverter converter = new JwtAccessTokenConverter(){
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
String grantType = authentication.getOAuth2Request().getGrantType();
//授权码和密码模式才自定义token信息
if(AUTHORIZATION_CODE.equals(grantType) || GRANT_TYPE_PASSWORD.equals(grantType)) {
String userName = authentication.getUserAuthentication().getName();
// 自定义一些token 信息
Map<String, Object> additionalInformation = new HashMap<String, Object>(16);
additionalInformation.put("user_name", userName);
additionalInformation = Collections.unmodifiableMap(additionalInformation);
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);
}
OAuth2AccessToken token = super.enhance(accessToken, authentication);
return token;
}
};
// 设置签署key
converter.setSigningKey("signingKey");
return converter;
} @Bean
public TokenStore jwtTokenStore() {
//基于jwt实现令牌(Access Token)
return new JwtTokenStore(accessTokenConverter());
} }

SpringSecurity配置类:

package com.example.springboot.oauth2.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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; /**
* <pre>
* Spring Security配置类
* </pre>
*
* <pre>
* @author mazq
* 修改记录
* 修改后版本: 修改人: 修改日期: 2020/06/15 10:39 修改内容:
* </pre>
*/
@Configuration
@EnableWebSecurity
@Order(1)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
} @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception { //auth.inMemoryAuthentication()
auth.inMemoryAuthentication()
.withUser("nicky")
.password("{noop}123")
.roles("admin");
} @Override
public void configure(WebSecurity web) throws Exception {
//解决静态资源被拦截的问题
web.ignoring().antMatchers("/asserts/**");
web.ignoring().antMatchers("/favicon.ico");
} @Override
protected void configure(HttpSecurity http) throws Exception {
http // 配置登录页并允许访问
.formLogin().permitAll()
// 配置Basic登录
//.and().httpBasic()
// 配置登出页面
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/")
.and().authorizeRequests().antMatchers("/oauth/**", "/login/**", "/logout/**").permitAll()
// 其余所有请求全部需要鉴权认证
.anyRequest().authenticated()
// 关闭跨域保护;
.and().csrf().disable();
} }

3、功能简单测试

访问授权链接,在浏览器访问就可以,授权码模式response_type参数传code:

http://localhost:8888/oauth/authorize?client_id=cms&client_secret=secret&response_type=code

因为没登录,所以会返回SpringSecurity的默认登录页面,具体代码是 http .formLogin().permitAll();,如果要弹窗登录的,可以配置http.httpBasic();,这种配置是没有登录页面的,自定义登录页面可以这样配置http.formLogin().loginPage("/login").permitAll()

如图,输入SpringSecurity配置的数据库密码

登录成功,返回redirect_uri,拿到授权码

重定向回redirect_uri,http://localhost:8084/cms/login?code=???

配置一下请求头的授权参数,用Basic Auth方式,username即client_id,password即client_secret



拿到授权码之后去获取token,本教程使用授权码方式



JWT方式的token

{
"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTIzNzYwNjEsInVzZXJfbmFtZSI6Im5pY2t5IiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9hZG1pbiJdLCJqdGkiOiJiM2IwZGExNS1mMmQyLTRlN2MtYTUwNC1iMzg5YjkxMjM0MDMiLCJjbGllbnRfaWQiOiJjbXMiLCJzY29wZSI6WyJhbGwiXX0.TpIBd9Gtb4M7sC1MSQsxsn8mwnhAm59CUBZPU7jwdnE",
"token_type":"bearer",
"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJuaWNreSIsInNjb3BlIjpbImFsbCJdLCJhdGkiOiJiM2IwZGExNS1mMmQyLTRlN2MtYTUwNC1iMzg5YjkxMjM0MDMiLCJleHAiOjE1OTIzNzYwNjEsImF1dGhvcml0aWVzIjpbIlJPTEVfYWRtaW4iXSwianRpIjoiODVhYTlmMGYtNDliNS00NDg4LTk4MTQtNmM0MmZjMjZkYTc2IiwiY2xpZW50X2lkIjoiY21zIn0.TU8ZD_5AxRGbgbOWZSuWAxwWjMJ4HLHniA46M-dnChE",
"expires_in":119,
"scope":"all",
"user_name":"nicky",
"jti":"b3b0da15-f2d2-4e7c-a504-b389b9123403"
}

例子代码下载:code download

OAuth2.0系列之使用JWT令牌实践教程(八)的更多相关文章

  1. OAuth2.0实战!使用JWT令牌认证!

    大家好,我是不才陈某~ 这是<Spring Security 进阶>的第3篇文章,往期文章如下: 实战!Spring Boot Security+JWT前后端分离架构登录认证! 妹子始终没 ...

  2. OAuth2.0系列之基本概念和运作流程(一)

    @ 目录 一.OAuth2.0是什么? 1.1 OAuth2.0简介 1.2 OAuth2.0官方文档 二.OAuth2.0原理 2.1 OAuth2.0流程图 三. OAuth2.0的角色 四.OA ...

  3. Spring Security系列之极速入门与实践教程

    @ 目录 1. Spring Security 2. 实验环境准备 3. 日志级别修改 4. 配置用户名/密码 5. 数据库方式校验 6. 不拦截静态资源 7. 自定义登录页面 8. Remember ...

  4. SpringCloud2.0 Hystrix Dashboard 断路器指标看板 基础教程(八)

    1.启动基础工程 1.1.启动[服务中心]集群,工程名称:springcloud-eureka-server 参考 SpringCloud2.0 Eureka Server 服务中心 基础教程(二) ...

  5. OAuth2.0实战:认证、资源服务异常自定义!

    大家好,我是不才陈某~ 这是<Spring Security 进阶>的第4篇文章,往期文章如下: 实战!Spring Boot Security+JWT前后端分离架构登录认证! 妹子始终没 ...

  6. Spring Security OAuth2.0认证授权三:使用JWT令牌

    Spring Security OAuth2.0系列文章: Spring Security OAuth2.0认证授权一:框架搭建和认证测试 Spring Security OAuth2.0认证授权二: ...

  7. Force.com微信开发系列(七)OAuth2.0网页授权

    OAuth是一个开放协议,允许用户让第三方应用以安全且标准的方式获取该用户在某一网站上存储的私密资源(如用户个人信息.照片.视频.联系人列表),而无须将用户名和密码提供给第三方应用.本文将详细介绍OA ...

  8. 妹子始终没搞懂OAuth2.0,今天整合Spring Cloud Security 一次说明白!

    大家好,我是不才陈某~ 周二发了Spring Security 系列第一篇文章,有妹子留言说看了很多文章,始终没明白OAuth2.0,这次陈某花了两天时间,整理了OAuth2.0相关的知识,结合认证授 ...

  9. OAuth2.0的原理介绍

    OAuth2.0是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版. OAuth2.0(开放授权)是一个正式的互联网标准协议. 允许第三方网站在用户 ...

  10. [渣译文] SignalR 2.0 系列:SignalR的服务器广播

    英文渣水平,大伙凑合着看吧…… 这是微软官方SignalR 2.0教程Getting Started with ASP.NET SignalR 2.0系列的翻译,这里是第八篇:SignalR的服务器广 ...

随机推荐

  1. ASP.NET Core中DI中Add*方法对类的假定

    在ASP.NET Core的依赖注入(DI)容器中,当你使用Add*方法和泛型类型来指定要注册的类时,容器会做出以下假设: (1)类必须是具体类(Concrete Class):使用Add*方法注册的 ...

  2. vue之sync

    在 Vue 中,.sync 是一个用于实现双向数据绑定的特殊修饰符.它允许父组件通过一种简洁的方式向子组件传递一个 prop,并在子组件中修改这个 prop 的值,然后将修改后的值反馈回父组件,实现双 ...

  3. 7 MyBatis动态SQL之bind标签|转

    1 MyBatis动态SQL之if 语句 2 MyBatis动态sql之where标签|转 3 MyBatis动态SQL之set标签|转 4 MyBatis动态SQL之trim元素|转 5 MyBat ...

  4. Spring注解之@Autowired:装配构造函数和属性

    在User类中创建一个构造函数,传入参数student: import org.springframework.beans.factory.annotation.Autowired; import o ...

  5. IDEA terminal控制台配置git bash及中文乱码问题

    1.修改控制台shell路径:setting -> Tools -> Terminal -> Shell path,修改为git安装路径. 修改完毕,在控制台输入exit断开sess ...

  6. prometheus 日常配置记录

    通用模糊匹配 irate(node_network_transmit_bytes_total{device!~"lo|bond[0-9]|cbr[0-9]|veth.*"}[5m] ...

  7. String在内存中如何分布

    一.设计思想及原理 设计思想 1.字符串分配和其他的对象分配一样,耗费高昂的时间与空间代价,作为最基础的数据类型,大量频繁的创建字符串,极大程度地影响程序的性能. 2.JVM为了提高性能和减少内存开销 ...

  8. 数栈SQL优化案例:OR条件优化

    本文整理自:袋鼠云技术荟 | SQL优化案例(2):OR条件优化 数栈是云原生-站式数据中台PaaS,我们在github上有一个有趣的开源项目:https://github.com/DTStack/f ...

  9. DRF案例

    1 反序列化更新,instance 就传要修改的对象,保证修改完成 def update(self, instance, validated_data): publish_id = validated ...

  10. Altair官方文档——HyperMesh的使用与帮助

    1.1.3 启动 HyperMesh (1) On PC • 从起始菜单,选择 All Programs >Altair HyperWorks (version) > HyperMesh ...