项目笔记之权限校验

Spring Security概述

1.1 Spring Security介绍

Spring Security 的前身是 Acegi Security ,是 Spring 项目组中用来提供安全认证服务的框架。

官网地址: https://projects.spring.io/spring-security/

Spring Security 为基于J2EE企业应用软件提供了全面安全服务。

特别是使用领先的J2EE解决方案-Spring框架开发的企业软件项目。人们使用Spring Security有很多种原因,不过通常吸

引他们的是在J2EE Servlet规范或EJB规范中找不到典型企业应用场景的解决方案。 特别要指出的是他们不能再

WAR 或 EAR 级别进行移植。这样,如果你更换服务器环境,就要,在新的目标环境进行大量的工作,对你的应用

系统进行重新配 置安全。使用Spring Security 解决了这些问题,也为你提供很多有用的,完全可以指定的其他安

全特性。 安全包括两个主要操作。

本质:

Spring Security 采用 AOP,以及基于 Filter过滤器实现的安全认证框架。它提供了完善的认证机构和授权功能,而在做系统的时候,一般做的第一个模块就是认证与授权模块,因为这是一个系统的入口,也是一个系统最重要最基础的一环,在认证与授权服务设计搭建好了之后,剩下的模块才得以安全访问。

小结:

  1. springSecurity基于spring构建的安全认证服务框架
  2. springSecurity基于Filter实现的安全认证服务框架(必须在web项目中).
  3. springSecurity基于aop实现认证和授权服务的(aop底层是动态代理)

1.2 登录认证(Authentication)和访问授权(Authorization)

  • 什么是登录认证

    指的获取用户输入的账号和密码信息,校验是否正确,这个过程就是认证过程.

  • 什么是访问授权

    指的登录认证通过后, 根据用户的账号信息获取该用户所匹配的资源数据, 让用户访问与之匹配的资源.

    比如: 资源数据指的用户访问的页面,或者用户访问controller里面的请求路径,执行controller里面的方法

  • **“登录认证”**指的是为用户建立一个他所声明的主体,主题一般式指用户,设备或可以在你系 统中执行动作的其他系统。

    1、基于表单的认证(Cookie & Session):基于表单的认证并不是在 HTTP 协议中定义的,而是服务器自己实现的认证方式,安全程度取决于实现程度。一般用 Cookie 来管理 Session 会话,是最常用的认证方式之一。它的安全程度取决于服务器的实现程度,客户端在Cookie中携带认证信息,服务器解析并返回结果。

    总结:

    1. 首次访问服务器端, 将用户信息 提交服务器端, 在服务器端对用户提交的信息进行认证,

      如果认证通过将认证过的信息存到cookie里面,同时向cookie响应到浏览器端

      比如: cookie里面存到信息: pass=“yes”

    2. 下次再访问时, 提交cookie到服务器端, 在服务器端获取cookie里面的信息, 判断是否已经认证,

    比如: 在服务器端获取cookie里面的信息: pass="yes",说明之前认证过了,不在认证
    

    2、基于JWT(Json Web Token)的认证:App和服务端常用的认证方式,用户ID和密码传输到服务器上验证,服务器验证通过以后生成加密的JWT Token返回给客户端,客户端再发起请求时携带返回的Token进行认证。(多了个防篡改)

    总结: 基于 JWT 令牌认证(三部分数据)认证过程,进行加密处理, 将认证后的数据以json格式形式保存到token.

    ​ 底层也是基于cookie实现的.

    1. 用户首次访问服务器端, 提交用户信息, 在服务器端进行认证, 认证通过过,将认证的信息以json格式保存到token

      底层: 将token保存到cookie里面, 将cookie响应到客户端.

    2. 下次再次访问服务器端时, 将客户端保存的cookie提交到服务器端, 在服务器端获取cookie, 解析里面的 token.

      如果token里面保存了之前的认证信息, 可以不再认证了

      比如: 基于jwt进行登录认证,可以对认证数据进行加密, 可以对认证数据设置过期时间.

    3、Http Basic 认证:最早的 Http 认证方式,用户 ID 和密码以分号连接,经过 Base64 编码后存储到 Authorization 字段,发送到服务端进行认证 ;用户 ID/密码 以明文形式暴露在网络上,安全性较差。(如果没有使用 SSL/TLS 这样的传输层安全的协议,那么以明文传输的密钥和口令很容易被拦截)

    4、Http Digest 认证:在 HttpBasic 的基础上,进行了一些安全性的改造,用户ID, 密码 , 服务器/客户端随机数,域,请求信息,经过 MD5 加密后存储到 Authorization 字段,发送到服务端进行认证;密码经过 MD5 加密,安全性比 Basic 略高。

    5、其他认证方式(Oauth 认证,单点登陆,HMAC 认证):通过特定的加密字段和加密流程,对客户端和服务端的信息进行加密生成认证字段,放在 Authorization 或者是消息体里来实现客户信息的认证

    • Oauth认证: 基于第三方进行认证, 比如: 登录csdn可以使用第三方账号,比如: 微信登录.

    • 单点登录: 一次登录,到处通行(指的登录一个网站,可以访问与之相关的其它网站,这时在访问其它网站时,不在认证)

      比如: 比如登录淘宝以后, 再访问天猫,或者聚划算, 或者 阿里云 不用再登录.

      单点登录的实现方式:

      比如基于cas进行单点登录: client端---------------搭建的单点登录服务器.

      1. 第一次访问淘宝, 输入账号信息, 将账号信息提交到单点登录服务器 进行认证,认证通过以后, 将认证通过后的信息存到cookie里面.

      2. 下次再访问 淘宝时,提供cookie到单点登录服务器, 获取cookie.解析认证信息,如果之前认证过了 , 不再认证.

      3. 下次再访问天猫时, 如果能提交之前保存认证信息的cookie到单点登录服务器, 直接认证通过.

        如果不能提交之前保存认证信息的cookie, 必须再次认证.

  • “访问授权” 指的是一个用户能否在你的应用中执行某个操作,在到达授权判断之前,身份的主题已经由 身份验证

过程建立了。

这些概念是通用的,不是Spring Security特有的。在身份验证层面,Spring Security广泛支持各种身份验证模式,

这些验证模型绝大多数都由第三方提供,或则正在开发的有关标准机构提供的,

例如 Internet Engineering TaskForce.作为补充,

Spring Security 也提供了自己的一套验证功能。

Spring Security 目前支持认证一体化如下认证技术:

HTTP BASIC authentication headers (一个基于IEFT RFC 的标准)

HTTP Digest authentication headers (一个基于IEFT RFC 的标准)

HTTP X.509 client certifificate exchange(一个基于IEFT RFC 的标准)

LDAP (一个非常常见的跨平台认证需要做法,特别是在大环境)

Form-based authentication (提供简单用户接口的需求)

OpenID authentication Computer Associates Siteminder JA-SIG Central Authentication Service (CAS,这是一个流行的开源单点登录系统)

Transparent authentication context propagation for Remote Method Invocation and HttpInvoker (一个Spring远程调用协议)

总结:

  1. 认证 (Authentication): 你是谁。

    Authentication(认证) 是验证您的身份的凭据(例如用户名/用户ID和密码),通过这个凭据,系统得以知道你就是你,也就是说系统存在你这个用户。所以,Authentication 被称为身份/用户验证。

  2. 授权 (Authorization): 你有权限干什么。

    Authorization(授权) 发生在 Authentication(认证) 之后。授权嘛,光看意思大家应该就明白,它主要掌管我们访问系统的权限。比如有些特定资源只能具有特定权限的人才能访问比如 admin,有些对系统资源操作比如删除、添加、更新只能特定人才具有。

    如图:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-narNHuWP-1663904910716)(img/image-20220921171735707.png)]

1.3 认证流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4X7ikler-1663904910718)(img/image-20220921171926863.png)]

这里的 AuthenticationFilter 采用的是责任链设计模式(请求层层上报,直到有人解决为止),一个 web 请求会经过一条过滤器链,在经过过滤器链的过程中会完成认证与授权,如果中间发现这条请求未认证或者未授权,会根据被保护 API 的权限去抛出异常,然后由异常处理器去处理这些异常。

过滤链如下图所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bucmaian-1663904910719)(img/image-20220921172001696.png)]

注:这里 Security 提供两种过滤器类:
UsernamePasswordAuthenticationFilter 表示表单登陆过滤器 BasicAuthenticationFilter 表示 httpBasic 方式登陆过滤器

如上图,一个请求想要访问到 API 就会以从左到右的形式经过蓝线框框里面的过滤器,其中绿色部分是负责认证的过滤器,蓝色部分负责异常处理,橙色部分则是负责授权。

不过上述的两个过滤器是 Spring Security 对 form 表单认证和 Basic 认证内置的两个 Filter.

1.4 认证核心组件(api)

  • SecurityContext:上下文对象,Authentication 对象会放在里面。
  • SecurityContextHolder:用于拿到上下文对象的静态工具类。
  • Authentication:认证接口,定义了认证对象的数据形式。
  • AuthenticationManager:用于校验 Authentication,返回一个认证完成后的 Authentication 对象
SecurityContext

上下文对象,认证后的数据就放在这里面,接口定义如下:

public interface SecurityContext extends Serializable {
   
 // 获取Authentication对象
 Authentication getAuthentication();

 // 放入Authentication对象
 void setAuthentication(Authentication authentication);
}
SecurityContextHolder

用于拿到上下文对象的静态工具类。

SecurityContextHolder 用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限… 这些都被保存在 SecurityContextHolder 中。SecurityContextHolder 默认使用 ThreadLocal 策略来存储认证信息。

public class SecurityContextHolder {
   

 public static void clearContext() {
   
  strategy.clearContext();
 }

 public static SecurityContext getContext() {
   
  return strategy.getContext();
 }

 public static void setContext(SecurityContext context) {
   
  strategy.setContext(context);
 }

}

可以说是 SecurityContext 的工具类,用于 get or set or clear SecurityContext,默认会把数据都存储到当前线程中。

从这个 SecurityContextHolder 取得 UserDetail 的实例:

public static String getLoginAccount() {
   
    // getPrincipal 返回值需要强转为 UserDetail 具体原因看下面的 Authentication那节
    return ((UserDetail) SecurityContextHolder
    .getContext()
    .getAuthentication() // 返回:Authentication
    .getPrincipal()) // 这里就是 Authentication 内部保存的 UserDetail 对象
    .getUsername();
}
Authentication

认证接口,定义了认证对象的数据形式。

public interface Authentication extends Principal, Serializable {
   

 Collection<? extends GrantedAuthority> getAuthorities();
 Object getCredentials();
 Object getDetails();

 // AuthenticationManager 实现通常会返回一个包含更丰富信息的 Authentication 作为供应用程序使用的主体。 
 // 许多身份验证提供程序将创建一个 UserDetails 对象作为主体
 // 所以如果 AuthenticationManager 使用的是 ProviderManager 则这里返回值需要强转为 UserDetails
 Object getPrincipal();

 boolean isAuthenticated();
 void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

这几个方法效果如下:

  • getAuthorities: 获取用户权限,一般情况下获取到的是用户的角色信息。
  • getCredentials: 获取证明用户认证的信息,通常情况下获取到的是密码等信息。
  • getDetails: 获取用户的额外信息,(这部分信息可以是我们的用户表中的信息)。
  • getPrincipal: 获取用户身份信息,在未认证的情况下获取到的是用户名,在已认证的情况下获取到的是 UserDetails。
  • isAuthenticated: 获取当前 Authentication 是否已认证。
  • setAuthenticated: 设置当前 Authentication 是否已认证(true or false)。

Authentication 只是定义了一种在 SpringSecurity 进行认证过的数据的数据形式应该是怎么样的,要有权限,要有密码,要有身份信息,要有额外信息。

AuthenticationManager ⭐

认证管理器很多,在这里不做过多介绍

public interface AuthenticationManager {
   
 // 认证方法
 Authentication authenticate(Authentication authentication)
   throws AuthenticationException;
}

AuthenticationManager (接口)是认证相关的核心接口,它定义了一个认证方法,它将一个未认证的 Authentication 传入,返回一个已认证的 Authentication。它的默认实现类是 ProviderManager

注:AuthenticationManager 有多个认证类 AuthenticationManager,ProviderManager ,AuthenticationProvider …

总结:

整合上面四个组件认证流程

将这四个部分,串联起来,构成 Spring Security 进行认证的流程:

1、先是一个请求带着身份信息进来,用户名和密码被过滤器获取到,封装成 Authentication,通常情况下是 UsernamePasswordAuthenticationToken 这个实现类。

2、这个 Authentication 经过 AuthenticationManager 的认证(身份管理器负责验证)认证成功后,AuthenticationManager 身份管理器返回一个被填充满了信息的(包括上面提到的权限信息,身份信息,细节信息,但密码通常会被移除)Authentication 实例。

3、SecurityContextHolder 安全上下文容器将上面填充了信息的 Authentication,通过 SecurityContextHolder.getContext().setAuthentication(…) 方法,设置到 SecurityContext 其中。

下面编写一个例子,具体的描述这个流程(不是源码, 只是模拟认证流程)

public class AuthenticationExample {
   
    private static final AuthenticationManager am = new SampleAuthenticationManager();

    public static void main(String[] args) throws Exception {
   
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

        while(true) {
   
            System.out.println("Please enter your username:");
            String name = in.readLine();
            System.out.println("Please enter your password:");
            String password = in.readLine();

            try {
   
                // 1、封装一个 UsernamePasswordAuthenticationToken 对象
                Authentication request = new UsernamePasswordAuthenticationToken(name, password);

                // 2、经过 AuthenticationManager 的认证,如果认证失败会抛出一个 AuthenticationException 错误
                Authentication result = am.authenticate(request);

                // 3、将这个认证过的 Authentication 填入 SecurityContext 里面
                SecurityContextHolder.getContext().setAuthentication(result);
                break;
            } catch(AuthenticationException e) {
   
                System.out.println("Authentication failed:" + e.getMessage());
            }
        }

        System.out.println("Successfully authenticated. Security context contains:\n" +
                SecurityContextHolder.getContext().getAuthentication());
    }
}

// 实现一个简单 AuthenticationManager 用于认证
class SampleAuthenticationManager implements AuthenticationManager {
   
    static final List<GrantedAuthority> AUTHORITIES = new ArrayList<>();

    static {
   
        AUTHORITIES.add(new SimpleGrantedAuthority("ROLE_USER"));
    }

    // 关键认证部分
    @Override
    public Authentication authenticate(Authentication auth) throws AuthenticationException {
   

        // getCredentials 返回的是密码,这里随便写了,直接用户名和密码一致就算登陆成功
        if (auth.getName().equals(auth.getCredentials())) {
   
            // 认证成功返回一个已经认证的 UsernamePasswordAuthenticationToken 的对象,并把这个用户的权限填入
            return new UsernamePasswordAuthenticationToken(auth.getName(),
                    auth.getCredentials(), AUTHORITIES);
        }
        throw new BadCredentialsException("Bad Credentials");
    }
}

1.5 授权流程

授权分析

认证成功之后会将当前登录用户信息保存到Authentication对象中,Authentication对象中有一个getAuthorities()方法,用来返回当前登录用户具备的权限信息,也就是当前用户具有权限信息。该方法的返回值为Collection<? extends GrantedAuthority>,当需要进行权限判断时,就会根据集合返回权限信息调用相应 方法进行判断。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-drXhUm3q-1663904910720)(img/image-20220921175038743.png)]

那么问题来了,针对于这个返回值GrantedAuthority应该如何理解?是角色还是权限?

基于角色进行权限管理

一般情况下: 设计权限表:

user表-user_role中间表–role角色表-----role_permission中间表 --permission权限表

实现方式:

在认证通过以后, 根据用户信息查询该用户所拥有的角色, 将用户认证信息和角色信息设置到springSecurity提供的UserDetails(比较流行)

基于资源进行权限管理 -->权限字符串

R(Role Resources) B(base) A(access) C(controll)

我们针对于授权可以是基于角色权限管理和基于资源权限管理,从设计层面上来说,角色和权限是两个完全不同的东西:权限是一些具体操作,角色则是某些权限集合。如:READ_BOOK和ROLE_ADMIN是完全不同的。因此至于返回值是什么取决于你的业务设计情况:

基于角色权限设计就是:用户<>角色<>资源 三者关系 返回就是用户的角色(用的多)

基于资源权限设计就是: 用户<>权限<>资源 三者关系 返回就是用户的权限

基于角色和资源权限设计就是: 用户<>角色<>权限<==>资源 返回统称为用户的权限

为什么统称为权限,因为从代码层面角色和权限没有太大不同都是权限,特别是在Spring Security中。角色和权限处理方式基本上都是一样的。唯一区别SpringSecurity在很多时候会自动给角色添加一个**ROLE_**前缀,而权限则不会自动添加。

权限管理策略

可以访问系统中哪些资源(http url method)

Spring Security中提供的权限管理策略主要有两种类型:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值