Spring Security、oauth2、单点登陆SSO的关系

概述

网上有很多Spring Security和 oauth2的介绍,但是对于初学者来说,上手比较复杂,本篇从原理上梳理一下两者之间的联系和区别

1. 什么是Spring Security

参见 【Spring Security】基本功能介绍

spring security 的核心功能主要包括:

  • 认证 (你是谁) 通过注解 @EnableWebSecurity开启

    简单来说,就是需要登录,你需要输入用户名和密码,才能访问某个url。

  • 授权 (你能干什么) 不需要通过指定的开关开启,而是通过配置来增加授权规则来生效,不增加授权规则就不生效

    授权的目的是可以把资源进行划分,例如公司有不同的资料,有普通级别和机密级别,只有公司高层才能看到机密级别的子类,而普通级别的资料大家都可以看到! 那么授权就是允许你查看某个资源,当然,如果你没有权限,就拒绝你查看!

    授权有个前提就是先认证才能授权,例如你是A公司的人,有A公司的卡牌,才能进入A公司资料库。如果是B公司的人,大门都不给你进。

认证可以单独使用,即不划分资源的级别,所有人只要登录都可以查看

1.1 配置示例

public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {

    //配置安全拦截策略
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //链式配置拦截策略
        http
                .csrf().disable()//关闭csrg跨域检查
                //这里注意matchere是有顺序的。
                .authorizeRequests()
                .antMatchers("/user/**").hasAuthority("user")
                .antMatchers("/order/**").hasAuthority("order")
                .antMatchers("/common/**").permitAll() //common下的请求直接通过
                .antMatchers("/**.html","/js/**","/css/**","/img/**").permitAll()//放行静态资源
                .anyRequest().authenticated() //其他请求需要登录
                .and()//并行条件
                .formLogin()
                .loginPage("/index.html")//自定义登录页面
                .loginProcessingUrl("")
                .defaultSuccessUrl("/main.html").failureUrl("/common/loginFailed"); //可从默认的login页面登录,并且登录后跳转到main.html
    }

简单来说:

  • 配置了 @EnableWebSecurity,就需要先登陆, .anyRequest().authenticated() 就是让你登陆的意思
  • 系统内置了登陆页面,一般情况下都是需要定制的,因此,通过 .loginPage(“/index.html”) 你可以指定自定义的登陆页
  • 由于有些资源是不需要登陆就行访问的,例如登陆页html,否则,形成死循环了。index.html等价于售票处,你总得让游客免票入内吧
  • .antMatchers(“/user/**”).hasAuthority(“user”)用于定制哪些请求有额外要求,例如景区内的厕所,先买大门票后,男的进男侧,不允许进女厕

1.2 spring security 基本原理

spring security的核心就是一组过滤器链,项目启动后将会自动配置。最核心的就是 Basic Authentication Filter 用来认证用户的身份,一个在spring security中一种过滤器处理一种认证方式。

如下图所示,这是一组链式处理器类,请求从做往右依次经过多个过滤器类处理:

在这里插入图片描述

比如,对于username password认证过滤器来说:

会检查是否是一个登录请求;

是否包含username 和 password (也就是该过滤器需要的一些认证信息) ;

如果不满足则放行给下一个。

下一个按照自身职责判定是否是自身需要的信息,basic的特征就是在请求头中有 Authorization:Basic eHh4Onh4 的信息。中间可能还有更多的认证过滤器。最后一环是 FilterSecurityInterceptor,这里会判定该请求是否能进行访问rest服务,判断的依据是 BrowserSecurityConfig中的配置,如果被拒绝了就会抛出不同的异常(根据具体的原因)。Exception Translation Filter 会捕获抛出的错误,然后根据不同的认证方式进行信息的返回提示。

注意:绿色的过滤器可以配置是否生效,其他的都不能控制。

1.2 Spring Security存在的问题

一般情况下 ,Spring Security 应用在一个单体项目中,此时只存在一个独立的项目,你访问一个资源,需要输入用户名和密码就行了

在多体应用中,特别是存在第三方的场景下,我们存在这样的需求:

  • 假设你去注册csdn的账户,那么需要填入手机号、昵称、邮箱等信息,有的繁琐,如果你已经有了微信账户的话,那么csdn可以从微信处获取这些信息,而你只需要通过手机扫一下就实现了,是不是很方便?
  • oauth2协议应运而生。你去访问csdn注册页面,然后页面转向微信登陆页面(授权中心角色),你输入正确的用户名和密码(手机扫码是输入用户名和密码的一种变形),然后csdn获得了token,带着token,访问微信的用户信息接口(资源中心角色),微信返回用户的邮箱等信息,csdn利用这些信息,完成注册新用户,入用户表

什么是单体应用?就是资源和检查权限的系统都正在一个地方,传统的注册csdn手动输入邮箱,然后你登陆csdn,就是单体应用,而我们去注册csdn,此时微信的个人信息数据就是第三方系统的,此时涉及csdn和微信2个系统

2. 什么是oauth2

此时一般的文章都是通过2个步骤来演示的,第一步 拿到token,通过浏览器发送一个请求,完成用户名密码登陆输入,模拟获取token过程,第二步 携带token获取资源,通过postman发送请求模拟查询资源

oauth2是一种协议,核心是用token令牌替换直接输入用户名和密码的方式,通过该协议,可以实现跨服务至之间的授权功能。一般分为2个模块,认证授权中心和资源中心,资源中心可以有多个:

  • 授权中心:负责颁发token,配置`@EnableAuthorizationServer

  • 资源中心:负责检查token(可以自己检查,例如jwt本地检查 或委托授权中心检查),检查通过后发放资源。

基础原理是这样的:

  • 授权中心颁发token
    这个没啥原理,就是让用户输入用户名和密码,检查下是否正确,然后返回一个token,说白了就是一个字符串,并存在授权中心的服务器上。

    就像你买的饭票一样,你用现金买了饭票,将来凭借饭票就可以领取一份盒饭!不需要再付钱了

  • 向资源中心发起一个资源的查询,资源中心检查token,检查通过后,发放资源。

    关键点在于微信颁发的token,可以给csdn,csdn带着token去微信获取资源,这个步骤是在csdn后台完成的,对用户来说是隐藏的。也就是说csdn拿了饭票,去微信领盒饭了,不需要付钱(输入密码),爽!

OAuth是Open Authority的缩写,是令牌代替用户密码访问应用的又一标准, OAuth 协议存在第一代和第二代之分,后者称为OAuth2,Spring OAuth2是实现OAuth2协议的具体的框架之一,你也可以使用其他的OAuth2框架。

对于大家而言,我们在互联网应用中最常见的 OAuth2 应该就是各种第三方登录了。利用OAuth2协议,我们在注册csdn账户的时候,可以直接使用微信或QQ账户进行注册,这样,仅需登陆微信或QQ,即可让csdn获取到用户的昵称、爱好、邮箱等基础信息

2.1 那么如何实现检查token的呢

答案在于 @EnableResourceServer注解,表示开启资源中心功能,会有代理类负责登陆和授权的检查:

如果某个服务的入口类被 @EnableResourceServer注解,说明现在是基于oauth2协议的架构,那么原来的Spring Security配置会被覆盖:
在这里插入图片描述
一般的Spring Security要求所有的请求url都要先判断是否登录,如果没有登录,就跳转至登陆页,然后检查用户名和密码是否正确,但是资源中心注解会内置有更高优先级的拦截器,会修改这个默认的逻辑,不是通过用户名和密码来检查是否正确,而是通过检查消息头中的Authorization:Bearer xxx参数。

开启资源中心,所有资源优先用token方式进行检查,即检查消息头中是否含有 Authorization:Bearer xxx 这样格式的;
如果没有token,直接判定失败;即使有了token,那么如何验证?可以本地验证,或转发token至授权中心进行判断

授权中心颁发token后,会把token存储在内存中,这样当ABC服务获得token后,转发至授权中心,和内存中存储的原始值进行比较就行了。

2.2 jwt

token默认是明文的不安全,采用jwt,可以进行加密,更安全!

加密有2种形式:

  • 对称加密

    缺点:容易破解

  • 非对称加密

    优点:不容易破解

2.3 endpoint概念

授权中心需要配置endpoint,那么endpoint是什么?

授权中心内置一些url,表示用于token特定的功能,这些url是免密使用的:

授权中心内置很多url资源,例如/oauth/token,这些url就是endpoint概念

在这里插入图片描述

你可以去复写/oauth/token 这样的url

2.4 spring Security和 spring Security oauth2关系

从第一章节,我们知道, spring Security是一个框架,提供了认证和授权功能,从本章得知oauth2是一个协议,光有协议也没用,你的有具体的实现,那么spring Security oauth2是什么东西呢?答案就是 spring Security框架内置了oauth2的api,可以直接使用。当然,其他框架也有oauth2的实现,你可以根据需求选择。

3. spring cloud oauth2

我们在第二章节用到的语法,Spring Security的oauth2 是内置在Spring Security包中的,例如@EnableAuthorizationServer 开启授权中心注解是在spring-security-oauth2-2.3.4.RELEASE.jar,说明Spring Security框架已经包含了oauth2协议的api接口:

从注解的package 能看出是springframework下的,说明是基础包:

package org.springframework.security.oauth2.config.annotation.web.configuration;
public @interface EnableAuthorizationServer {
}

spring cloud oauth2是提供了一个starter,自动引入相关的依赖包,仅仅是方便使用,并没有新增特殊的功能:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>

4. sso单点登陆

通过前面的学习,我们知道,利用token原理,可以实现登陆一个系统后,只输入一次用户名和密码,然后在cookie中保存这个token,再访问其他系统时,带上这个token,就可以直接访问资源了。

oauth2的基本原理就是利用了token实现的,帮助我们利用第三方系统获得资源。

单点登录的英文名是 Single Sign On,因此一般简称为SSO。它的用途在于,不管多么复杂的应用群,只要在用户权限范围内,那么就可以做到,用户只需要登录一次就可以访问权限范围内的所有应用子系统。对于用户而言,访问多个应用子系统只需要登录一次,同样在需要注销的时候也只需要注销一次。举个简单的例子,你在百度首页登录成功之后,你再访问百度百科、百度知道、百度贴吧等网站也会处于登录状态了,这就是一个单点登录的真实案例。

划分为2类角色:

  • 授权服务器 负责检查是否授权及生成token
  • 资源服务器,提供资源,如果发现没有登陆,统一重定向至授权中心进行token授权。多台资源服务器可以共享同一个token,不用每次都生成token

特点是 检查是否有权限的操作是在资源服务器上,即每个资源服务器都有检查的逻辑。

缺点:sso默认配置仅能解决登陆认证问题,但是解决不了授权问题,即/user 和/order需要不同的权限

单点登陆和Oauth2的区别和联系:

  • 相似点都是利用token作为介质
  • 单点登陆侧重多个子系统的场景,登陆一个,另几个系统免登陆,简单来说就是同一个公司下的不同子系统,而oauth2侧重存在一个第三方系统,彼此完全独立,简单理解就是不同公司旗下的不同系统。
  • 单点登陆侧重认证,即检查是否登陆,一般不细分权限,即只要登陆即可,所有的功能都可以访问。如果有角色概念,那么单点登陆就不能完全满足,需要额外的工作去负责授权检查;oauth2侧重认证+授权,属于单一url的,一次只能访问特定的资源,如果访问多个资源,需要多个token
  • sso实际上是扩展了oauth2的功能,即扩展了oauth2协议的,使之用于某个特殊场景,即带有子系统的一个大系统!

也就是说不同公司之间的系统,采用oauth2就是完美方案!
如果是同一个公司的不同子系统,那么不考虑复杂的授权的场景下,单点登陆就是完美方案!
如果单点登陆同时需要考虑授权的情况,那么就要考虑授权功能,系统就更复杂了,需要sso+授权,例如下一章节就是该问题的解决方案!

5. 统一网关

从前面学习的示例来看,是不包含网关的,即sso项目是多个独立服务都要进行鉴权,每个服务都要配置一套鉴权系统,只不过是把具体的处理流程转发至同一个鉴权中心了,只要登录一次,就可以在多个服务中心共享登录信息。那么有了网关之后怎么办?

有了网关,可以在网关处进行鉴权,后续的请求转发至具体服务后,后者不再需要进行鉴权了,或者网关暂不处理鉴权,转发请求至具体的服务后,由服务自行进行鉴权

有了网关之后,就有了统一的入口!

如果项目集成了网关,在网关里整合 OAuth2.0,实现单点登陆 有两种思路:

  • 一种是授权服务器生成令牌, 所有请求统一在网关层验证,判断权限等操作;

    优点: 只有网关层一个模块负责检查权限,资源模块都不用涉及
    缺点:需要改造,对技术要求高

  • 另一种是由各资源服务处理,网关只做请求转发。 这种情况下,不用做特殊处理,等价于第四章节的sso单点登陆

    优点:保留现有架构不动,每个资源服务配置sso即可
    缺点:每个资源服务配置sso,架构上不好,存在冗余的感觉

比较常用的是第一种,把API网关作为OAuth2.0的资源服务器角色,实现接入客户端权限拦截、令牌解析并转发当前登录用户信息给微服务,这样下游微服务就不需要关心令牌格式解析以及OAuth2.0相关机制了。

网关在认证授权体系里主要负责两件事:
(1)作为OAuth2.0的资源服务器角色,实现接入方权限拦截。
(2)令牌解析并转发当前登录用户信息(明文token)给微服务

微服务拿到明文token(明文token中包含登录用户的身份和权限信息)后也需要做两件事:
(1)用户授权拦截(看当前用户是否有权访问该资源)
(2)将用户信息存储进当前线程上下文(有利于后续业务逻辑随时获取当前用户信息)

  • 61
    点赞
  • 149
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值