OAuth + Security -1 - 认证服务器配置
配置
基础包依赖
<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 {
}
}
- 配置客户端详细信息
ClientDetailsServiceConfigurer能够使用内存或者JDBC来实现客户端详情服务
(ClientDetailsService ) , ClientDetailsService负责查找ClientDetails ,而ClientDetails有几个重要的属性如下列表:
- clientld: (必须的)用来标识客户的Id。
- secret : ( 需要值得信任的客户端)客户端安全码,如果有的话。
- scope :用来限制客户端的访问范围,如果为空(默认)的话,那么客户端拥有全部的访问范围。
- authorizedGrantTypes :此客户端可以使用的授权类型,默认为空。
- authorities :此客户端可以使用的权限(基于Spring Security authorities )。
客户端详情( ClientDetails )能够在应用程序运行的时候进行更新,可以通过访问底层的存储服务(例如
将客户端详情存储在一个关系数据库的表中,就可以使用JdbcClientDetailsService或者通过自己实现
ClientRegistrationService接口(同时你也可以实现ClientDetailsService接口)来进行管理。
- 令牌访问端点和令牌服务配置
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
以上的参数都是以"/"开始的字符串
- 配置令牌端点的安全约束
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
- 首先获取授权码
在浏览器输入 http://127.0.0.1:8080/oauth/authorize?client_id=dimples&response_type=code&redirect_uri=http://www.baidu.com
跳转到登录界面,然后进行用户登录,登录成功以后选择用户授权,获取相应的授权码,将会显示在重定向URL的链接后面。如下图:
- 获取code
- 验证用户
- 授权
- 得到code
- 拿着这个获取的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 - 认证服务器配置的更多相关文章
- Spring Security构建Rest服务-1204-Spring Security OAuth开发APP认证框架之Token处理
token处理之一基本参数配置 处理token时间.存储策略,客户端配置等 以前的都是spring security oauth默认的token生成策略,token默认在org.springframe ...
- Spring Security构建Rest服务-1300-Spring Security OAuth开发APP认证框架之JWT实现单点登录
基于JWT实现SSO 在淘宝( https://www.taobao.com )上点击登录,已经跳到了 https://login.taobao.com,这是又一个服务器.只要在淘宝登录了,就能直接访 ...
- OAuth 2.0 认证的原理与实践
摘要: 使用 OAuth 2.0 认证的的好处是显然易见的.你只需要用同一个账号密码,就能在各个网站进行访问,而免去了在每个网站都进行注册的繁琐过程. 本文将介绍 OAuth 2.0 的原理,并基于 ...
- 最简单易懂的Spring Security 身份认证流程讲解
最简单易懂的Spring Security 身份认证流程讲解 导言 相信大伙对Spring Security这个框架又爱又恨,爱它的强大,恨它的繁琐,其实这是一个误区,Spring Security确 ...
- Spring Cloud实战 | 第九篇:Spring Cloud整合Spring Security OAuth2认证服务器统一认证自定义异常处理
本文完整代码下载点击 一. 前言 相信了解过我或者看过我之前的系列文章应该多少知道点我写这些文章包括创建 有来商城youlai-mall 这个项目的目的,想给那些真的想提升自己或者迷茫的人(包括自己- ...
- OAuth做webapi认证
OAuth做webapi认证 看到园子里面有人写的OAuth,就想把自己实现的OAuth也分享一下,关于OAuth协议这里就不再赘述. 一.作为认证服务器,首先需要提供一个可以通过appid/apps ...
- ASP.NET Core 认证与授权[3]:OAuth & OpenID Connect认证
在上一章中,我们了解到,Cookie认证是一种本地认证方式,通常认证与授权都在同一个服务中,也可以使用Cookie共享的方式分开部署,但局限性较大,而如今随着微服务的流行,更加偏向于将以前的单体应用拆 ...
- Spring Security 接口认证鉴权入门实践指南
目录 前言 SpringBoot 示例 SpringBoot pom.xml SpringBoot application.yml SpringBoot IndexController SpringB ...
- Spring Security自定义认证器
在了解过Security的认证器后,如果想自定义登陆,只要实现AuthenticationProvider还有对应的Authentication就可以了 Authentication 首先要创建一个自 ...
随机推荐
- 线段树 区间加 gcd 差分 小阳的贝壳
小阳的贝壳 如果线段树要维护区间gcd 这个很简单,但是如果有了区间加,维护gcd 就比较麻烦了. 这个首先可以证明的是 gcd(x,y,z)=gcd(x,y-x,z-y) 这个可以推到 n 个 ...
- P2762 太空飞行计划问题 网络流
题目描述 W 教授正在为国家航天中心计划一系列的太空飞行.每次太空飞行可进行一系列商业性实验而获取利润.现已确定了一个可供选择的实验集合E={E1,E2,…,Em},和进行这些实验需要使用的全部仪器的 ...
- MySQL(二)MySQL中的存储引擎
前言 数据库存储引擎是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建.查询.更新和删除数据.不同的存储引擎提供不同的存储机制.索引技巧.锁定水平等功能,使用不同的存储引擎,还可以 ...
- Day_14【IO流】扩展案例2_缓冲字符输出、输入流进行用户名的创建
需求分析 1.项目根目录下建立文件: user.txt,文件中存放用户名和登录密码,格式:用户名,密码,如:aaa,123: 2.user.txt文件中初始存放的用户信息有如下: jack,123 r ...
- [hdu5313]二分图性质,dp
题意:给定二分图,求添加的最多边数,使得添加之后还是二分图 思路:如果原图可以分成X,Y两个点集,那么边数最多为|X||Y|条.由于|X|+|Y|==n,所以需要使|X|与|Y|尽量接近.先对原图进行 ...
- [hdu5213]容斥原理+莫队算法
题意:给一个序列a,以及K,有Q个询问,每个询问四个数,L,R,U,V, 求L<=i<=R,U<=j<=V,a[i]+a[j]=K的(i, j)对数(题目保证了L <= ...
- python 一个模块找不到的错误:ModuleNotFoundError
阿刁是一个自动化测试用例,从一出生他就被赋予终生使命,去测试一个叫登录的过程是否合理.他一直就被关在一个小黑屋里面,从来也没有出去过,小黑屋里还被关着其他的同胞,他们身上都捆着两个小袋子. 小黑屋里很 ...
- 配置centos7 java环境
一.环境 centos7 jdk-8u231-linux-x64.tar.gz 二.安装jdk 使用ftp或者 WinScp软件把下载在win10电脑上的jdk安装包上传到linux 解压到/opt/ ...
- python爬虫-vmgirls-正则表达式
概述 本次爬虫任务是爬取图片网站图片,网址是https://www.vmgirls.com/ 分析网页 第一步,打开需要爬取的页面https://www.vmgirls.com/13344.html ...
- React知识点整理
面试题:三大框架中数据绑定实现上有何绑定? 一.概述:是Facebook维护的一个构建用户界面的JS库,核心很精简,但是生态圈扩展很大. React:MVVM框架 React-Router:路由 Re ...