文章部分图片来自参考资料,这篇文章主要讲 spring security  oauth

概述

上一篇我们学习了 SS 中重要的工作原理和几个大概的认证和授权过程。而 spring security oauth 用到的就是 spring security 知识,我们学习 sso 之前先看一下oauth 是什么,可以学习阮一峰老师的文章

oauth 的流程图如下 : (牢牢记住这张图)

主要的角色有资源持有者,资源服务器,认证服务器,还有用户

授权(获取 Access Token)的方式有多种方式

  • 授权码
  • 简化模式
  • 客户端模式
  • 密码模式

oauth 可以理解成工作中,你(Client)去出差,回来需要报销,会计(Authorzation Server)首先需要你请示老板(Resource Owned)是否同意给你报销出差费用,假如同意了,你就回来找会计,把老板的凭证给她,她会给你一个token (获取token过程的方式有多种,就是前面提到的), 然后你带着 token 再去财务(Resource Server)领钱 ,结束流程。

Spring Security Oauth

    学习 Spring Security Oauth  ,先学习一个例子(出处),然后根据例子配合oauth 流程学习

我们按照上面的例子敲完代码后,整个流程走完再结合oauth 授权的流程

例子中使用的授权码,而获取Access Token ,为何先给授权码,而不直接给 Access Token 呢 ?

给授权码,再用授权码去获取Access Token 的原因是授权码可以让服务端知道client 的身份。

spring security oauth 角色

oauth 获取中几个重要的角色中在 spring security oauth 中对应的有 :

  • @EnableResourceServer : 作为资源服务器
  • @EnableAuthorazaitonServer : 作为认证中心
  • @EnableOauthClient :做用被认证的客户端,例如提供某个方式去认证 Github 或是 Facebook 的应用

Resource Owned 的角色放在 Authorazation Server,就是代码中的 UserDetail 。上面三个注解经常会混淆,我们需要记住它们到底实现的用途是什么,还有另外一个注解 : @EnableSSO

UserDetail

UserDetail 的作用是用来认证即是上面oauth 流程图的A 步骤,示例如下 :

@Service
public class MyUserDetailsService implements UserDetailsService { @Autowired
private UserService userService; /**
* 授权的时候是对角色授权,而认证的时候应该基于资源,而不是角色,因为资源是不变的,而用户的角色是会变的
*/ @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser sysUser = userService.getUserByName(username);
if (null == sysUser) {
throw new UsernameNotFoundException(username);
}
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (SysRole role : sysUser.getRoleList()) {
for (SysPermission permission : role.getPermissionList()) {
authorities.add(new SimpleGrantedAuthority(permission.getCode()));
}
} return new User(sysUser.getUsername(), sysUser.getPassword(), authorities);
}
} @Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
... @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());
} }

TokenStore

获取token,那么很明显需要一个储存token 的容器,例如我们想使用 Redis 来存储token ,当我们需要实现自己token 存储容器时可以如下使用 :

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { ... /**
* 该方法是用来配置Authorization Server endpoints的一些非安全特性的,比如token存储、token自定义、授权类型等等的
* 默认情况下,你不需要做任何事情,除非你需要密码授权,那么在这种情况下你需要提供一个AuthenticationManager
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.tokenStore(new MyRedisTokenStore(redisConnectionFactory));
} } @Service
public class MyRedisTokenStore implements TokenStore {
.... }

clientDetail

使用授权码方式获取 Access Token 时先发放授权码,而是否可以发送授权码需要验证client 的身份,clientDetail 便是便是表述 client 基本信息的类

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { ... @Resource
private DataSource dataSource; /**
* 配置ClientDetailsService
* 注意,除非你在下面的configure(AuthorizationServerEndpointsConfigurer)中指定了一个AuthenticationManager,否则密码授权方式不可用。
* 至少配置一个client,否则服务器将不会启动。
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
} }

token 和 clientDetail 基类表述

可以参考此处构建自己的 token 和 clientDetail ,以下是数据库实现

-- used in tests that use HSQL
create table oauth_client_details (
client_id VARCHAR(256) PRIMARY KEY,
resource_ids VARCHAR(256),
client_secret VARCHAR(256),
scope VARCHAR(256),
authorized_grant_types VARCHAR(256),
web_server_redirect_uri VARCHAR(256),
authorities VARCHAR(256),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information VARCHAR(4096),
autoapprove VARCHAR(256)
); create table oauth_client_token (
token_id VARCHAR(256),
token LONGVARBINARY,
authentication_id VARCHAR(256) PRIMARY KEY,
user_name VARCHAR(256),
client_id VARCHAR(256)
); create table oauth_access_token (
token_id VARCHAR(256),
token LONGVARBINARY,
authentication_id VARCHAR(256) PRIMARY KEY,
user_name VARCHAR(256),
client_id VARCHAR(256),
authentication LONGVARBINARY,
refresh_token VARCHAR(256)
); create table oauth_refresh_token (
token_id VARCHAR(256),
token LONGVARBINARY,
authentication LONGVARBINARY
); create table oauth_code (
code VARCHAR(256), authentication LONGVARBINARY
); create table oauth_approvals (
userId VARCHAR(256),
clientId VARCHAR(256),
scope VARCHAR(256),
status VARCHAR(10),
expiresAt TIMESTAMP,
lastModifiedAt TIMESTAMP
); -- customized oauth_client_details table
create table ClientDetails (
appId VARCHAR(256) PRIMARY KEY,
resourceIds VARCHAR(256),
appSecret VARCHAR(256),
scope VARCHAR(256),
grantTypes VARCHAR(256),
redirectUrl VARCHAR(256),
authorities VARCHAR(256),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additionalInformation VARCHAR(4096),
autoApproveScopes VARCHAR(256)
);

完整服务端配置

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired
private MyUserDetailsService myUserDetailsService; @Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/oauth/**","/login/**", "/logout").permitAll()
.anyRequest().authenticated() // 其他地址的访问均需验证权限
.and()
.formLogin()
.loginPage("/login")
.and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/login");
} @Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**");
} @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());
} @Bean
@Override
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
} @Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
} }
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Resource
private DataSource dataSource; @Autowired
RedisConnectionFactory redisConnectionFactory; @Autowired
private AuthenticationManager authenticationManager; /**
* 配置授权服务器的安全,意味着实际上是/oauth/token端点。
* /oauth/authorize端点也应该是安全的
* 默认的设置覆盖到了绝大多数需求,所以一般情况下你不需要做任何事情。
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
super.configure(security);
} /**
* 配置ClientDetailsService
* 注意,除非你在下面的configure(AuthorizationServerEndpointsConfigurer)中指定了一个AuthenticationManager,否则密码授权方式不可用。
* 至少配置一个client,否则服务器将不会启动。
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
} /**
* 该方法是用来配置Authorization Server endpoints的一些非安全特性的,比如token存储、token自定义、授权类型等等的
* 默认情况下,你不需要做任何事情,除非你需要密码授权,那么在这种情况下你需要提供一个AuthenticationManager
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.tokenStore(new MyRedisTokenStore(redisConnectionFactory));
}
}

当资源服务器和认证服务器是同一个服务器的时候  :

Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired
private MyUserDetailsService myUserDetailsService; @Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/oauth/**","/login/**", "/logout").permitAll()
.anyRequest().authenticated() // 其他地址的访问均需验证权限
.and()
.formLogin()
.loginPage("/login")
.and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/login");
} @Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**");
} @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());
} @Bean
@Override
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
} @Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
} }

工作流程

http://blog.didispace.com/xjf-spring-security-1/

http://blog.didispace.com/xjf-spring-security-1/

总结

文章并不是 spring oauth 的入门篇,主要是结合 oauth 的流程图找到对应 spring security oauth 框架中的逻辑对应,更好地自定义改造。结合下面的参考资料,可以完成单点登录的功能。

参考资料

Spring boot --- Spring Oauth(一)的更多相关文章

  1. 使用Spring Boot和OAuth构建安全的SPA

    最近一段时间都在闭关学习,过程还是有点艰辛的,幸运的是还有优锐课老师带着,少走了很多弯路.很久也没有更新文章了,这篇想和大家分享的是,了解如何在使用Spring Boot入门程序的同时使用Spring ...

  2. spring Boot+spring Cloud实现微服务详细教程第二篇

    上一篇文章已经说明了一下,关于spring boot创建maven项目的简单步骤,相信很多熟悉Maven+Eclipse作为开发常用工具的朋友们都一目了然,这篇文章主要讲解一下,构建spring bo ...

  3. spring Boot+spring Cloud实现微服务详细教程第一篇

    前些天项目组的大佬跟我聊,说项目组想从之前的架构上剥离出来公用的模块做微服务的开发,恰好去年的5/6月份在上家公司学习了国内开源的dubbo+zookeeper实现的微服务的架构.自己平时对微服务的设 ...

  4. Spring boot +Spring Security + Thymeleaf 认证失败返回错误信息

    [Please make sure to select the branch corresponding to the version of Thymeleaf you are using] Stat ...

  5. 255.Spring Boot+Spring Security:使用md5加密

    说明 (1)JDK版本:1.8 (2)Spring Boot 2.0.6 (3)Spring Security 5.0.9 (4)Spring Data JPA 2.0.11.RELEASE (5)h ...

  6. 256.Spring Boot+Spring Security: MD5是加密算法吗?

    说明 (1)JDK版本:1.8 (2)Spring Boot 2.0.6 (3)Spring Security 5.0.9 (4)Spring Data JPA 2.0.11.RELEASE (5)h ...

  7. Spring Boot+Spring Security:获取用户信息和session并发控制

    说明 (1)JDK版本:1.8(2)Spring Boot 2.0.6(3)Spring Security 5.0.9(4)Spring Data JPA 2.0.11.RELEASE(5)hiber ...

  8. Cola Cloud 基于 Spring Boot, Spring Cloud 构建微服务架构企业级开发平台

    Cola Cloud 基于 Spring Boot, Spring Cloud 构建微服务架构企业级开发平台: https://gitee.com/leecho/cola-cloud

  9. spring boot + spring batch 读数据库文件写入文本文件&读文本文件写入数据库

    好久没有写博客,换了一家新公司,原来的公司用的是spring,现在这家公司用的是spring boot.然后,项目组布置了一个任务,关于两个数据库之间的表同步,我首先想到的就是spring batch ...

  10. Spring Boot/Spring Cloud、ESB、Dubbo

    如何使用Spring Boot/Spring Cloud 实现微服务应用spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中的配置管理.服务发现. ...

随机推荐

  1. ASP.NET Core 2 学习笔记(四)依赖注入

    ASP.NET Core使用了大量的依赖注入(Dependency Injection, DI),把控制反转(Inversion Of Control, IoC)运用的相当巧妙.DI可算是ASP.NE ...

  2. C# 多线程 弹出模态MessageBox的一种方法

    在多线程中,有时候使用MessageBox.Show方法弹出对话框,弹出的Messagebox不是模态的,不能满足我的要求.经过研究,如下实现 private void button1_Click(o ...

  3. WPF画辐射图

    public void WriteLineCircle(double originX, double originY, double r, int lineCount,List<string&g ...

  4. 关于在C++中调用R函数以及RCpp使用

    最近因为项目要用到,所以在想办法把R语言用到C++中. 网上查了看到有一个Rcpp的工具.所以在这里总结一下. 现在能想到的几种在C++中调用R语言的方法如下: 1. 使用Rcpp R高级编程技巧及R ...

  5. Tomcat 服务器开启失败故障

    Server Tomcat v8.0 Server at localhost failed to start.故障 原因:1.可能是web.xml中的filter-mapping中url-patter ...

  6. 程序媛计划——python初级class5~13

    列表和元组都是可迭代对象(可以用于for in) 列表 [] #添加列表元素: list.append(argu) #修改列表: list[2] = 2017 #删除列表元素 Del list[2] ...

  7. Depth-first Search-690. Employee Importance

    You are given a data structure of employee information, which includes the employee's unique id, his ...

  8. 利用nginx实现负载均衡

    1.安装nginx和tomcat 我这里是使用docker安装的.安装流程可参照 dockerfile 这里安装了两个tomcat,端口分别是42000和42001.第二个tomcat的首页随便加了些 ...

  9. Java自学路线

    万事开头难,学习Java亦是如此.而在学习的开始,选择正确的学习路线更是尤为重要.在本文中我将分享本人自学转行路上的学习路线,希望能给想自学,却不知道方向的同学带来帮助~ 1 .JavaSE 基础 这 ...

  10. 奇怪的Java题:为什么1000 == 1000返回为False,而100 == 100会返回为True?

    如果你运行如下代码: 1 2 3 4 Integer a = 1000, b = 1000;  System.out.println(a == b);//1 Integer c = 100, d =  ...