配置

基础包依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
================================== 在spring-boot中 ==================================
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.5.RELEASE</version>
</dependency>
================================== 或者在spring-cloud中 ==================================
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

配置三大核心

认证服务器的配置需要继承 AuthorizationServerConfigurerAdapter 类,然后重写内部的方法来获得自己逻辑的token,其源码如下:

public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {

    // 配置安全约束,主要是对默认的6个端点的开启与关闭配置
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
} // 客户端信息配置
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
} // 认证服务器令牌访问端点配置和令牌服务配置,可以替换默认的端点url,配置支持的授权模式
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
} }
  1. 配置客户端详细信息

ClientDetailsServiceConfigurer能够使用内存或者JDBC来实现客户端详情服务

(ClientDetailsService ) , ClientDetailsService负责查找ClientDetails ,而ClientDetails有几个重要的属性如下列表:

  • clientld: (必须的)用来标识客户的Id。
  • secret : ( 需要值得信任的客户端)客户端安全码,如果有的话。
  • scope :用来限制客户端的访问范围,如果为空(默认)的话,那么客户端拥有全部的访问范围。
  • authorizedGrantTypes :此客户端可以使用的授权类型,默认为空。
  • authorities :此客户端可以使用的权限(基于Spring Security authorities )。

客户端详情( ClientDetails )能够在应用程序运行的时候进行更新,可以通过访问底层的存储服务(例如

将客户端详情存储在一个关系数据库的表中,就可以使用JdbcClientDetailsService或者通过自己实现

ClientRegistrationService接口(同时你也可以实现ClientDetailsService接口)来进行管理。

  1. 令牌访问端点和令牌服务配置

AuthorizationServerEndpointsConfigurer这个对象的实例可以完成令牌服务以及令牌endpoint配置。

配置授权类型( Grant Types )

AuthorizationServerEndpointsConfigurer通过设定以下属性决定支持的授权类型( Grant Types ) :

  • authenticationManager:认证管理器,当你选择了资源所有者密码(password)授权类型的时候,请设置这个属性注入一个AuthenticationManager对象。
  • userDetailsService :如果你设置了这个属性的话,那说明你有一个自己的UserDetailsService接口的实现,或者你可以把这个东西设置到全局域上面去(例如GlobalAuthenticationManagerConfigurer这个配置对象),当你设置了这个之后,那么"refresh_token"即刷新令牌授权类型模式的流程中就会包含一个检查,用来确保这个账号是否仍然有效,假如说你禁用了这个账户的话。
  • authorizationCodeServices:这个属性是用来设置授权码服务的(即AuthorizationCodeServices的实例对象) ,主要用于"authorization_ code" 授权码类型模式。
  • implicitGrantService :这个属性用于设置隐式授权模式,用来管理隐式授权模式的状态。
  • tokenGranter :当你设置了这个东西(即TokenGranter接口实现),那么授权将会交由你来完全掌控,并且会忽略掉上面的这几个属性,这个属性一般是用作拓展用途的,即标准的四种授权模式已经满足不了你的需求的时候,才会考虑使用这个。
配置授权端点的URL

默认的端点URL有以下6个:

  • /oauth/authorize : 授权端点。
  • /oauth/token : 令牌端点。
  • /oauth/confirm _access : 用户确认授权提交端点。
  • /oauth/error : 授权服务错误信息端点。
  • /oauth/check_token : 用于资源服务访问的令牌解析端点。
  • /oauth/token_key : 提供公有密匙的端点,如果你使用JWT令牌的话。

这些端点都可以通过配置的方式更改其路径,在AuthorizationServerEndpointsConfigurer中有一个叫pathMapping()的方法用来配置端点URL链接,他有两个参数:

  • 第一个参数:string类型,默认的URL
  • 第二个参数:string类型,进行替代的URL

以上的参数都是以"/"开始的字符串

  1. 配置令牌端点的安全约束

AuthorizationServerSecurityConfigurer : 用来配置令牌端点(Token Endpoint)的安全约束,如配置资源服务器需要验证token的/oauth/check_token

总结

授权服务配置分成三大块,可以关联记忆。

既然要完成认证,它首先得知道客户端信息从哪儿读取,因此要进行客户端详情配置。

既然要颁发token,那必须得定义token的相关endpoint,以及token如何存取,以及客户端支持哪些类型的token。

既然暴露除了一些endpoint,那对这些endpoint可以定义一些安全上的约束等。

详细代码如下:

  • 认证服务器配置类
/**
* @author zhongyj <1126834403@qq.com><br/>
* @date 2020/6/1
*/
@Configuration
@EnableAuthorizationServer
public class DimplesAuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { private PasswordEncoder passwordEncoder; private TokenStore tokenStore; private ClientDetailsService clientDetailsService; private AuthenticationManager authenticationManager; @Autowired
public DimplesAuthorizationServerConfiguration(PasswordEncoder passwordEncoder, TokenStore tokenStore, ClientDetailsService clientDetailsService, AuthenticationManager authenticationManager) {
this.passwordEncoder = passwordEncoder;
this.tokenStore = tokenStore;
this.clientDetailsService = clientDetailsService;
this.authenticationManager = authenticationManager;
} /**
* 令牌端点的安全约束
*
* @param security AuthorizationServerSecurityConfigurer
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()")
.allowFormAuthenticationForClients();
} /**
* 客户端详情服务,暂时配置在内存中,后期改为存在数据库
*
* @param clients ClientDetailsServiceConfigurer
* @throws Exception Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("dimples")
.secret(this.passwordEncoder.encode("123456"))
// 为了测试,所以开启所有的方式,实际业务根据需要选择
.authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
.accessTokenValiditySeconds(3600)
.refreshTokenValiditySeconds(864000)
.scopes("select")
// false跳转到授权页面,在授权码模式中会使用到
.autoApprove(false)
// 验证回调地址
.redirectUris("http://www.baidu.com");
} /**
* 令牌访问端点和令牌访问服务
*
* @param endpoints AuthorizationServerEndpointsConfigurer
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
// /oauth/token_key公开
.authenticationManager(authenticationManager)
// /oauth/check_token公开
.authorizationCodeServices(authorizationCodeServices())
// 允许表单获取token
.tokenServices(tokenServices()); } /**
* 令牌管理服务
*
* @return TokenServices
*/
@Bean
public AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices services = new DefaultTokenServices();
// 客户端详情服务
services.setClientDetailsService(clientDetailsService);
// 支持令牌刷新
services.setSupportRefreshToken(true);
// 令牌存储策略
services.setTokenStore(tokenStore);
// 令牌默认有效期2小时
services.setAccessTokenValiditySeconds(7200);
// 刷新令牌默认有效期2天
services.setRefreshTokenValiditySeconds(259200);
return services;
} /**
* 设置授权码模式的授权码如何存储,暂时采用内存
*
* @return AuthorizationCodeServices
*/
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
return new InMemoryAuthorizationCodeServices();
} }
  • Token存储配置类
/**
* @author zhongyj <1126834403@qq.com><br/>
* @date 2020/6/1
*/
@Configuration
public class TokenConfigure { @Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
} }
  • Security 安全配置
/**
* @author zhongyj <1126834403@qq.com><br/>
* @date 2020/6/1
*/
@Configuration
public class DimplesWebSecurityConfiguration extends WebSecurityConfigurerAdapter { private DimplesUserDetailServiceImpl userDetailsService; private PasswordEncoder passwordEncoder; @Autowired
public DimplesWebSecurityConfiguration(DimplesUserDetailServiceImpl userDetailsService, PasswordEncoder passwordEncoder) {
this.userDetailsService = userDetailsService;
this.passwordEncoder = passwordEncoder;
} /**
* 安全拦截机制(最重要)
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/oauth/**").permitAll()
.anyRequest().authenticated()
.and().formLogin();
} /**
* 非必须配置,可以不配
* 认证管理配置
* 连接数据查询用户信息,与数据库中密码比对
*
* @param auth AuthenticationManagerBuilder
* @throws Exception Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
} /**
* 认证管理区配置,密码模式需要用到
*
* @return AuthenticationManager
* @throws Exception Exception
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
} }
  • 配置密码
@Configuration
public class DimplesConfigure { @Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
} }
  • 用户信息获取类
@Configuration
public class DimplesUserDetailServiceImpl implements UserDetailsService { private PasswordEncoder passwordEncoder; @Autowired
public DimplesUserDetailServiceImpl(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
} @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 模拟一个用户,替代数据库获取逻辑
DimplesUser user = new DimplesUser();
user.setUserName(username);
user.setPassword(this.passwordEncoder.encode("123456"));
// 输出加密后的密码
System.out.println(user.getPassword()); return new User(username, user.getPassword(), user.isEnabled(),
user.isAccountNonExpired(), user.isCredentialsNonExpired(),
user.isAccountNonLocked(), AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
}
} @Data
public class DimplesUser implements Serializable {
private static final long serialVersionUID = 3497935890426858541L; private String userName; private String password; private boolean accountNonExpired = true; private boolean accountNonLocked= true; private boolean credentialsNonExpired= true; private boolean enabled= true;
}

可能出现的问题

  • 最可能出现的问题,项目启动时报循环依赖的错误,要注意代码中在当前配置的类中写的Bean不能再当前类中去使用@Autowired注入
  • 在登录时控制台报错堆栈溢出

错误追踪:

是由于配置密码模式下的AuthenticationManager时,方法名称为authenticationManager,更改为authenticationManagerBean即可

测试

授权码模式下获取token

  1. 首先获取授权码

    在浏览器输入 http://127.0.0.1:8080/oauth/authorize?client_id=dimples&response_type=code&redirect_uri=http://www.baidu.com

跳转到登录界面,然后进行用户登录,登录成功以后选择用户授权,获取相应的授权码,将会显示在重定向URL的链接后面。如下图:

  1. 获取code

  1. 验证用户

  1. 授权

  1. 得到code

  1. 拿着这个获取的code的值去/oauth/token端点获取token,如下图:

这个code有错将不能获取到token,因为这个将会保存在程序内存中,同时这个code只能获取一次token

密码模式

/oauth/token?client_id=dimples&client_secret=123456&grant_type=password&username=dimples&password=123456

这种模式十分简单,但是也意味着直接将用户的敏感信息泄露给了client端,因此这种模式一般只用于client是我们自己开发的情况下。

客户端模式

/oauth/token?client_id=dimples&client_secret=123456&grant_type=client_credentials

简化模式使用较少,也比较简单,此处不做测试。

OAuth + Security -1 - 认证服务器配置的更多相关文章

  1. Spring Security构建Rest服务-1204-Spring Security OAuth开发APP认证框架之Token处理

    token处理之一基本参数配置 处理token时间.存储策略,客户端配置等 以前的都是spring security oauth默认的token生成策略,token默认在org.springframe ...

  2. Spring Security构建Rest服务-1300-Spring Security OAuth开发APP认证框架之JWT实现单点登录

    基于JWT实现SSO 在淘宝( https://www.taobao.com )上点击登录,已经跳到了 https://login.taobao.com,这是又一个服务器.只要在淘宝登录了,就能直接访 ...

  3. OAuth 2.0 认证的原理与实践

    摘要: 使用 OAuth 2.0 认证的的好处是显然易见的.你只需要用同一个账号密码,就能在各个网站进行访问,而免去了在每个网站都进行注册的繁琐过程. 本文将介绍 OAuth 2.0 的原理,并基于 ...

  4. 最简单易懂的Spring Security 身份认证流程讲解

    最简单易懂的Spring Security 身份认证流程讲解 导言 相信大伙对Spring Security这个框架又爱又恨,爱它的强大,恨它的繁琐,其实这是一个误区,Spring Security确 ...

  5. Spring Cloud实战 | 第九篇:Spring Cloud整合Spring Security OAuth2认证服务器统一认证自定义异常处理

    本文完整代码下载点击 一. 前言 相信了解过我或者看过我之前的系列文章应该多少知道点我写这些文章包括创建 有来商城youlai-mall 这个项目的目的,想给那些真的想提升自己或者迷茫的人(包括自己- ...

  6. OAuth做webapi认证

    OAuth做webapi认证 看到园子里面有人写的OAuth,就想把自己实现的OAuth也分享一下,关于OAuth协议这里就不再赘述. 一.作为认证服务器,首先需要提供一个可以通过appid/apps ...

  7. ASP.NET Core 认证与授权[3]:OAuth & OpenID Connect认证

    在上一章中,我们了解到,Cookie认证是一种本地认证方式,通常认证与授权都在同一个服务中,也可以使用Cookie共享的方式分开部署,但局限性较大,而如今随着微服务的流行,更加偏向于将以前的单体应用拆 ...

  8. Spring Security 接口认证鉴权入门实践指南

    目录 前言 SpringBoot 示例 SpringBoot pom.xml SpringBoot application.yml SpringBoot IndexController SpringB ...

  9. Spring Security自定义认证器

    在了解过Security的认证器后,如果想自定义登陆,只要实现AuthenticationProvider还有对应的Authentication就可以了 Authentication 首先要创建一个自 ...

随机推荐

  1. 网络流 A - PIGS POJ - 1149 最大流

    A - PIGS POJ - 1149 这个题目我开始感觉很难,然后去看了一份题解,写的很好 https://wenku.baidu.com/view/0ad00abec77da26925c5b01c ...

  2. 数位dp H - F(x) HDU - 4734

    http://acm.hdu.edu.cn/showproblem.php?pid=4734 一般数位dp表示的是数的性质,这个题目也是一样,但是我们求出来的是一个函数的值,怎么把这个值转化成一类数, ...

  3. RabbitMQ的轮询模式和公平分发

    一.常用的消息模式 我们在工作的使用中,经常会遇到多个消费者监听同一个队列的情况,模型如下图所示: 当有多个消费者时,我们的消息会被哪个消费者消费呢,我们又该如何均衡消费者消费信息的多少呢: 主要有两 ...

  4. Spring Batch 读 10 万条记录,写到 MongoDB

    实践内容 从 MariaDB 一张表内读 10 万条记录,经处理后写到 MongoDB . 具体实现 1.新建 Spring Boot 应用,依赖如下: <!-- Web 应用 --> & ...

  5. 【Scala】代码实现Actor多种需求

    文章目录 简单实现Actor并发编程 使用Actor实现发送没有返回值的异步消息 使用Actor实现不间断消息发送 用react方法替代receive方法接收消息 结合case class,通过匹配不 ...

  6. 【Spark】一起了解一下大数据必不可少的Spark吧!

    目录 Spark概述 官网 Spark是什么? 特点 Spark架构模块 主要架构模块 Spark Core Spark SQL Spark Streaming MLlib GraghX 集群管理器 ...

  7. 【Hadoop离线基础总结】oozie任务串联

    目录 需求 1.准备工作目录 2.准备调度文件 3.开发调度的配置文件 4.上传资源文件夹到hdfs对应路径 5.执行调度任务 需求 执行shell脚本 → 执行MR程序 → 执行hive程序 1.准 ...

  8. 自动化测试工具-Selenium IDE 教程一

    引言:这里介绍的是谷歌浏览种的插件,安装教程这里不再描述,网上有很多, 使用教程不是特别多,所以特地花时间整理此篇内容: 一:打开插件,欢迎界面 启动IDE后,将显示一个欢迎对话框. 如果这是您第一次 ...

  9. 帝国cms 批量替换 字段内容包含的 指定的 关键字 SQL命令

    帝国cms 批量替换 字段内容包含的 指定的 关键字update phome_ecms_news_data_1 set newstext=replace(newstext,'原来','现在');

  10. Java基础之值传递

    一.传递类型 我们从c语言开始学习程序设计语言时就知道,参数的传递类型一般有两种:值传递和引用传递.那么什么是值传递什么是引用传递呢? 值传递:指在调用方法时将实际参数的值拷贝一份传递给方法,这样方法 ...