百度
360搜索
搜狗搜索

springsecurity权限控制,Springboot security oauth2 jwt实现权限控制,实现微服务获取当前用户信息详细介绍

本文目录一览: Springboot security oauth2 jwt实现权限控制,实现微服务获取当前用户信息

在原先dubbo+zookeeper项目中,web模块只暴露Restful接口,各服务模块只暴露duboo接口,此时用户登录后由web项目进行token的鉴权和验证,并通过dubbo的隐式传参将sessionID传递给dubbo服务模块, 拦截器再根据sessionID从Redis中获取用户信息设置到当前线程

然鹅,在springcloud中,各个微服务直接暴露的是restful接口,此时如何让各个微服务获取到当前用户信息呢?最佳的方式就是token了,token作为BS之间的会话标识(一般是原生随机token),同时也可以作为信息的载体传递一些自定义信息(jwt, 即Json web token)。

为了能更清楚的了解本文,需要对spring-security-oauth 及 jwt有一定了解,本文只关注用户信息传递这一块

认证服务器配置 AuthorizationServerConfigurerAdapter

自定义token转换器
CustomJwtAccessTokenConverter

此时按照固定格式访问授权服务器token接口获取token,如图,可以获取到jwt格式的token,并且额外信息nick_name也已经添加

直接解析jwt字符串可以获取到以下信息,即用户名和授权信息

只需要指定和授权服务器一模一样的token store 和token converter
在securiy的过滤器中 OAuth2AuthenticationProcessingFilter 会从token中获取相关信息进行鉴权
源码:

注意,资源服务器主要配置在
ResourceServerConfigurerAdapter

微服务获取jwttoken中的用户信息,两种方式,使用security上下文可以直接获取当前用户名和权限,另一种自定义拦截器获取额外信息。
这个就简单了,获取header头解析验证token
然后获取之前从授权服务器中的添加的 nick_name的额外信息放入线程变量

其中用户上下文类

启动拦截器注册webmvc配置类

在controller中获取用户信息如图

在默认的认证异常如图

假设我们做了全局异常处理,前端希望在token过期时做统一的登录跳转如何做?
实现 AuthenticationEntryPoint 接口重写 commence 方法即可
注意,直接抛出异常并不会走 @RestControllerAdvice , 因为在这里是response直接返回,并没有使用到Controller处理

此时返回我自定义的Response对象,如图

Spring Security 中的权限注解很神奇吗?

最近有个小伙伴在微信群里问 Spring Security 权限注解的问题:
很多时候事情就是这么巧,松哥最近在做的 tienchin 也是基于注解来处理权限问题的,所以既然大家有这个问题,咱们就一块来聊聊这个话题。
先来看看 Spring Security 权限注解的具体用法,如下:
类似于上面这样,意思就是说,当前用户需要具备 tienchin:channel:query 权限,才能执行当前的接口方法。
那么要搞明白 @PreAuthorize 注解的原理,我觉得得从两个方面入手:
我们一个一个来看。
Spring Expression Language(简称 SpEL)是一个支持查询和操作运行时对象导航图功能的强大的表达式语言。它的语法类似于传统 EL,但提供额外的功能,最出色的就是函数调用和简单字符串的模板函数。
SpEL 给 Spring 社区提供一种简单而高效的表达式语言,一种可贯穿整个 Spring 产品组的语言。这种语言的特性基于 Spring 产品的需求而设计,这是它出现的一大特色。
在我们离不开 Spring 框架的同时,其实我们也已经离不开 SpEL 了,因为它太好用、太强大了,SpEL 在整个 Spring 家族中也处于一个非常重要的位置。但是很多时候,我们对它的只了解一个大概,其实如果你系统的学习过 SpEL,那么上面 Spring Security 那个注解其实很好理解。
我先通过一个简单的例子来和大家捋一捋 SpEL。
为了省事,我就创建一个 Spring Boot 工程来和大家演示,创建的时候不用加任何额外的依赖,就最最基础的依赖即可。
代码如下:
expressionStr 是我们自定义的一个表达式字符串,这个字符串通过一个 ExpressionParser 对象将之解析为一个 Expression,接下来就可以执行这个 exp 了。
执行的时候有两种方式,对于我们上面这种不带任何额外变量的,我们可以直接执行,直接执行的方式如下:
这个打印结果为 3。
我记得之前有个小伙伴在群里问想执行一个字符串表达式,但是不知道怎么办,js 中有 eval 函数很方便,我们 Java 中也有 SpEL,一样也很方便。
不过很多时候,我们要执行的表达式可能比较复杂,这时候上面这种调用方式就不太够用了。
此时我们可以为要调用的表达式设置一个上下文环境,这个时候就会用到 EvaluationContext 或者它的子类,如下:
当然上面这个表达式不需要设置上下文环境,我举一个需要设置上下文环境的例子。
例如我现在有一个 User 类,如下:
现在我的表达式是这样:
这个表达式就表示获取 user 对象的 username 属性。将来创建一个 user 对象,放到 StandardEvaluationContext 中,并基于此对象执行表达式,就可以打印出来想要的结果。
如果我们将 user 对象设置为 rootObject,那么表达式中就不需要 user 了,如下:
表达式就一个 username 字符串,将来执行的时候,会自动从 user 中找到 username 的值并返回。
当然表达式也可以是方法,例如我在 User 类中添加如下两个方法:
我们就可以通过表达式调用这两个方法,如下:
调用有参的 sayHello:
就直接写方法名然后执行就行了。
调用无参的 sayHello:
这些就都好懂了。
甚至,我们的表达式也可以涉及到 Spring 中的一个 Bean,例如我们向 Spring 中注册如下 Bean:
然后通过 SpEL 表达式来调用这个名为 us 的 bean 中的 sayHello 方法,如下:
给配置的上下文环境设置一个 bean 解析器,这个 bean 解析器会自动跟进名字从 Spring 容器中找打响应的 bean 并执行对应的方法。
当然,关于 SpEL 的玩法还有很多,我就不一一列举了。这里主要是想让小伙伴们知道,有这么个技术,方便大家理解 @PreAuthorize 注解的原理。
接下来我们就回到 Spring Security 中来看 @PreAuthorize 注解。
权限的实现方式千千万,又有各种不同的权限模型,然而归结到代码上,无非两种:
松哥之前的 vhr 使用的是前者。
@PreAuthorize 注解当然对应的是后者。这次做的 tienchin 项目就是后者,我们来看一个例子:
注解好说,里边的 @ss.hasPermi('tienchin:channel:query') 是啥意思呢?
这个 hasPermi 方法的逻辑其实很简单:
这个判断逻辑很简单,就是获取到当前登录的用户,判断当前登录用户的权限集合中是否具备当前请求所需要的权限。具体的判断逻辑没啥好说的,就是看集合中是否存在某个字符串。
那么这个方法是在哪里调用的呢?
大家知道,Spring Security 中处理权限的过滤器是 FilterSecurityInterceptor,所有的权限处理最终都会来到这个过滤器中。在这个过滤器中,将会用到各种投票器、表决器之类的工具,这里我就不细说了,之前的 Spring Security 系列教程都有详细介绍。
在投票器中,我们可以看到专门处理 @PreAuthorize 注解的类 PreInvocationAuthorizationAdviceVoter,我们来看下他里边的核心方法:
框架的源码写的就是好,你一看名字就知道他想干嘛了!这里就进入到最后一句,调用了一个 Advice 中到前置通知,来判断权限是否满足:
现在,当你看到这个 before 方法的时候,应该会觉得比较熟悉了吧。
就这样,是不是很简单?
好啦,今天就和小伙伴们分享这么多,在松哥近期推出的 tienchin 项目视频中,也会通过视频的形式跟大家细聊这个知识点。

五 spring security 其他权限检验及自定义校验方法

我们前面都是使用@PreAuthorize注解,然后在在其中使用的是hasAuthority方法进行校验。除此之外,spring security还为我们提供了其它方法例如:hasAnyAuthority,hasRole,hasAnyRole等。

hasAuthority原码:
hasAuthority方法底层是执行了SecurityExpressionRoot类的hasAuthority方法,它内部其实是调用authentication的getAuthorities方法获取用户的权限列表,然后判断我们传入的参数是否在权限列表中。

hasAnyAuthority方法可以传入多个权限,只要用户有其中任意一个权限都可以访问对应资源。

hasRole要求有对应的角色才可以访问,但是它内部会把我们传入的参数拼接上 ROLE_ 后再去比较,所以这种情况下要求用户对应的权限也要有 ROLE_ 这个前缀才可以。

hasAnyRole有任意的角色就可以访问,它内部也会把我们传入的参数拼接上 ROLE_ 后再去比较,所以这种情况下也要求用户对应的权限也要有 ROLE_ 这个前缀才可以。

自定义自己的权限校验方法

在SPEL表达式中使用 @ex相当于获取容器中bean的名字为ex的对象,然后再调用这个对象的hasAuthority方法

我们也可以在配置类中使用使用配置的方式对资源进行权限控制

如何在SpringBoot Security中实现OAuth2授权

OAuth2已经成为了一个授权的标准协议,大家在很多的产品中都能看到它的身影,比如我们登录一个网站,支持微信,QQ等登录方式,这里QQ和微信提供了授权服务。有很多的授权组件都能提供这种OAuth2认证方式,比如keycloak等,但我们也可以在SpringBoot security中实现OAuth2授权,这篇文章将详细讲解每一个步骤来演示如何在SpringBoot中实现OAuth2授权。

OAuth 2 是一种授权协议,用于通过 HTTP 协议提供对受保护资源的访问。OAuth2 使第三方应用程序能够获得对资源的有限访问。资源的所有者告诉系统,同意授权第三方应用进入系统,获取对这些资源访问。系统从而产生一个短期的进入令牌(token),用来代替密码,供第三方应用使用。
由于授权的场景众多,OAuth 2.0 协议定义了获取令牌的四种授权方式,分别是:

四种授权模式分别使用不同的 grant_type 来区分

OAuth 定义了四个角色

访问令牌的职责是在数据过期之前访问数据。
刷新令牌的职责是在现有访问令牌过期时请求新的访问令牌。

要使用 spring security oauth2 模块创建授权服务器,我们需要使用注解@EnableAuthorizationServer 并扩展类 AuthorizationServerConfigurerAdapter。

Spring security oauth 公开了两个用于检查令牌的端点(/oauth/check_token 和 /oauth/token_key),默认它们在 denyAll() 之后受保护。 tokenKeyAccess() 和 checkTokenAccess() 方法打开这些端点以供使用。
ClientDetailsServiceConfigurer 用于定义客户端详细信息,可以基于内存或 JDBC 实现。 为了演示的简单,例子中使用内存实现。 它具有以下重要属性:
clientId –(必需)客户端 ID。
secret(密钥) -(受信任的客户端需要)客户端密钥,如果有的话。
scope – 客户端受限的范围。 如果范围未定义或为空(默认),则客户端不受范围限制。
authorizedGrantTypes – 授权客户端使用的授权类型。 默认值为空。
权限 - 授予客户端的权限(常规 Spring Security 权限)。
redirectUris – 将用户代理重定向到客户端的重定向url。 它必须是绝对 URL。

阅读更多 >>>  mysql有哪些特点,mysql特点及优点

要创建资源服务器组件,需要使用 @EnableResourceServer 注释并扩展 ResourceServerConfigurerAdapter 类。

上面的配置在 /api 开始的所有端点上启用保护。 所有其他端点都可以自由访问。
资源服务器还提供了一种机制来验证用户本身。 在大多数情况下,它将是基于表单的登录。

上面的 WebSecurityConfigurerAdapter 类设置了一个基于表单的登录页面,并使用 permitAll() 打开授权 URL。

为了简化的目的,仅提供了一个简单的获取用户profile的服务,如下:

我们在浏览器中访问 http://localhost:8080/api/users/me ,会跳转到SpringBoot security提供的登录页面,输入用户名jack,密码123456就可以访问该api,但作为第三方应用程序,没有用户名和密码,只能通过OAuth2令牌来访问

如上面的流程图所示,第一步是从 URL 获取资源所有者的授权,如下的url

它将跳转到Springboot security提供的login页面,用户提供用户名和密码,我们的例子中提供用户名jack,密码123456

上一步获取到了授权码,下一步第三方应用程序将使用授权码来获取访问令牌。 可以使用curl命令来获取access token。

authorization中需要使用basic认证,提供用户名clientapp,密码123456,可以使用下面的网站来获取加密后的字符串

授权服务将返回如下的信息,包括access_token,refresh_token和token_type等。

获得访问令牌后,我们可以前往资源服务器获取受保护的API.使用curl命令来访问API,authorization是上一步获取到的access_token。

获取信息如下:

「干货」SpringBoot+SpringSecurity+Jwt权限认证-认证

启动项目时,SpringBoot自动检索所有带@Configuration的注解,所以就将我们的WebSecurityConfig给加载了,这个config中,我们需要在configure(AuthenticationManagerBuilder auth)方法中注册一个继承自UserDetailsService的接口,这个接口中只有一个方法,那就是使用username获取到数据库中用户信息并返回成UserDetail实体。这个方法需要我们按照我们的不同业务场景重写

WebSecurityConfig

MyUserDetailsService

其实如果去掉上面的将自定义的JWT过滤器加入到过滤链中的话,这个认证过程已经完成了。使用下面的代码就可以调用起整个认证程序。

核心代码

这一行就会将username和password放到认证程序中进行认证。

也就是需要我们自己的逻辑让他去触发这个代码的实现。就可以自动完成认证程序了。就会触发使用username获取到数据库用户信息,然后经过密码加密比对之后会将认证结果返回。

我们整合JWT其实也很简单,其实就是将JWT的登录部分的操作,使用过滤器封装,将该过滤器放到整个认证的过滤链中

SpringSecurity过滤器的配置无非以下几个条件

先解决逻辑上以上三个问题的答案

针对以上解答,下面用代码来做展示(ps:序号依次对应上面)

完成了以上的配置,前台就可以使用/login/user来进行登录操作了。登录成功会返回一个JSON对象来供前端判断成功与否

全部代码奉上,随意写的注释有点多,不看的可以给删掉

Spring Security+Spring Actuator实践

Spring Security用于安全认证

Spring Actuator用于应用监控

在实际项目中,两者结合使用的一些注意事项,总结如下:

endpoints.sensitive 用于控制 actuator 端点,security.basic 用于控制 Controller 层接口,二者互不影响

针对 actuator 的权限控制

1. endpoints.sensitive 和? management.security.enabled 有一个关闭,则无需鉴权,例如:

endpoints.sensitive = false 所有端点打开,即无需密码验证,即使 management.security.enabled=true,也配置了 security.user,也无需鉴权。

endpoints.sensitive = true 所有端点需要验证,但 management.security.enabled=false,即使配置了 security.user,也无需鉴权。

2. endpoints.sensitive 和? management.security.enabled 都打开的情况下:

1)配置了 security.user,使用对应用户名/密码可打开

2)未配置 security.user,则一定打不开

3. 若需单独开启或关闭某个端点,则使用 endpoints.端点名.属性名=true/false,例如: endpoints.info.sensitive=false

注: actuator 端点权限控制与 security.basic.enabled 无关。以上规则,对于单个端点的开关,同样适用。

针对 Controller 层接口权限控制

1. security.basic.enabled=false,则所有接口无需鉴权,即使配置了 security.user

2. security.basic.enabled=true,也配置了 security.user,则看 security.basic.path,则只有该path中的接口需要鉴权,默认为所有接口

3. 若需要单独控制某个接口,则使用 security.basic.path=/config/qryUser 多个时以逗号隔开

注: Controller 层接口权限控制与 endpoints.sensitive 和? management.security.enabled 无关

基于Spring Security Oauth2的SSO单点登录+JWT权限控制实践

理论知识
在此之前需要学习和了解一些前置知识包括:
要完成的目标
基于此目标驱动,本文设计三个独立服务,分别是:
多模块(Multi-Module)项目搭建
三个应用通过一个多模块的 Maven项目进行组织,其中项目父 pom中需要加入相关依赖如下:
项目结构如下:

项目结构
授权认证中心搭建
授权认证中心本质就是一个 Spring Boot应用,因此需要完成几个大步骤:
即让授权中心服务启动在本地的 8085端口之上
这里创建了一个用户名为codesheep,密码 123456的模拟用户,并且赋予了 普通权限 (ROLE_NORMAL)和 中等权限 (ROLE_MEDIUM)
这里做的最重要的两件事: 一是 定义了两个客户端应用的通行证(sheep1和sheep2); 二是 配置 token的具体实现方式为 JWT Token。
客户端应用创建和配置
本文创建两个客户端应用:codesheep-client1 和codesheep-client2,由于两者类似,因此只以其一为例进行讲解
复杂的东西都交给注解了!
这里几项配置都非常重要,都是需要和前面搭建的授权中心进行通信的
此测试控制器包含三个接口,分别需要三种权限(ROLE_NORMAL、ROLE_MEDIUM、ROLE_ADMIN),待会后文会一一测试看效果
实验验证
首先用浏览器访问客户端1 (codesheep-client1) 的测试接口:localhost:8086/normal,由于此时并没有过用户登录认证,因此会自动跳转到授权中心的登录认证页面:http://localhost:8085/uac/login:

自动跳转到授权中心统一登录页面
输入用户名 codesheep,密码 123456,即可登录认证,并进入授权页面:

授权页面
同意授权后,会自动返回之前客户端的测试接口:

自动返回客户端接口并调用成功
此时我们再继续访问客户端1 (codesheep-client1) 的测试接口:localhost:8086/medium,发现已经直接可以调用而无需认证了:

直接访问
由于 localhost:8086/normal 和 localhost:8086/medium要求的接口权限,用户codesheep均具备,所以能顺利访问,接下来再访问一下更高权限的接口:localhost:8086/admin:

无权限访问
好了,访问客户端1 (codesheep-client1) 的测试接口到此为止,接下来访问外挂的客户端2 (codesheep-client2) 的测试接口:localhost:8087/normal,会发现此时会自动跳到授权页:

由于用户已通过客户端1登录过_因此再访问客户端2即无需登录_而是直接跳到授权页
授权完成之后就可以顺利访问客户端2 (codesheep-client2) 的接口:

顺利访问客户端2的接口
这就验证了单点登录SSO的功能了!

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

Web API 接口服务场景里,用户的认证和鉴权是很常见的需求,Spring Security 据说是这个领域里事实上的标准,实践下来整体设计上确实有不少可圈可点之处,也在一定程度上印证了小伙们经常提到的 “太复杂了” 的说法也是很有道理的。
本文以一个简单的 SpringBoot Web 以应用为例,重点介绍以下内容:
创建 SpringBoot 示例,用于演示 Spring Security 在 SpringBoot 环境下的应用,简要介绍四部分内容:pom.xml、application.yml、IndexController 和 HelloController。
boot-example 是用于演示的 SpringBoot 项目子模块(Module)。
SpringBoot 应用名称为 example,实例端口为 9999 。
IndexController 实现一个接口:/。
HelloController 实现两个接口:/hello/world 和 /hello/name。
编译启动 SpringBoot 应用,通过浏览器请求接口,请求路径和响应结果:
SpringBoot 示例准备完成。
SpringBoot 集成 Spring Security 仅需要在 pom.xml 中添加相应的依赖: spring-boot-starter-security ,如下:
编译启动应用,相对于普通的 SpringBoot 应用,我们可以在命令行终端看到 特别 的两行日志:
表示 Spring Security 已在 SpringBoot 应用中生效。 默认 情况下,Spring Security 自动化 地帮助我们完成以下三件事件:
使用 Spring Security 默认为我们生成的用户名和密码进行登录(Sign in),成功之后会自动重定向至 / :
之后我们就可以通过浏览器正常请求 /hello/world 和 /hello/name。
默认情况下,Spring Security 仅支持基于 FormLogin 方式的认证,只能使用固定的用户名和随机生成的密码,且不支持鉴权。如果想要使用更丰富的安全特性:
本文以 Java Configuration 的方式为例进行介绍,需要我们提供一个继承自 WebSecurityConfigurerAdapter 配置类,然后通过重写若干方法进而实现自定义配置。
SecurityConfig 使用 @Configuration 注解(配置类),继承自 WebSecurityConfigurerAdapter ,本文通过重写 configure 方法实现自定义配置。
需要注意: WebSecurityConfigurerAdapter 中有多个名称为 configure 重载方法,这里使用的是参数类型为 HttpSecurity 的方法。
注:Spring Security 默认自动化配置参考 Spring Boot Auto Configuration 。
用以指定哪些请求需要什么样的认证或授权,这里使用 anyRequest() 和 authenticated() 表示所有的请求均需要认证。
表示我们使用 HttpBasic 认证。
编译启动应用,会发现终端仍会输出密码:
因为,我们仅仅改变的是认证方式。
为方便演示,我们使用 CURL 直接请求接口:
会提示我们 Unauthorized ,即:没有认证。
我们按照 HttpBasic 要求添加请求头部参数 Authorization ,它的值:
即:
再次请求接口:
认证成功,接口正常响应。
使用默认用户名和随机密码的方式不够灵活,大部分场景都需要我们支持多个用户,且分别为他们设置相应的密码,这就涉及到两个问题:
对于 读取 ,Spring Security 设计了 UserDetailsService 接口:
实现按照用户名(username)从某个存储介质中加载相对应的用户信息(UserDetails)。
用户名,客户端发送请求时写入的用于用户名。
用户信息,包括用户名、密码、权限等相关信息。
注意:用户信息不只用户名和用户密码。
对于 存储 ,Spring Security 设计了 UserDetailsManager 接口:
创建用户信息
修改用户信息
删除用户信息
修改当前用户的密码
检查用户是否存在
注意: UserDetailsManager 继承自 UserDetailsService 。
也就是说,我们可以通过提供一个已实现接口 UserDetailsManager * 的类,并重写其中的若干方法,基于某种存储介质,定义用户名、密码等信息的存储和读取逻辑;然后将这个类的实例以 Bean 的形式注入 Spring Security,就可以实现用户名和密码的自定义。
实际上,Spring Security 仅关心如何 读取 , 存储 可以由业务系统自行实现;相当于,只实现接口 UserDetailsService 即可。
Spring Security 已经为我们预置了两种常见的存储介质实现:
InMemoryUserDetailsManager和 JdbcUserDetailsManager 均实现接口 UserDetailsManager ,本质就是对于 UserDetails 的 CRUD 。我们先介绍 UserDetails ,然后再分别介绍基于内存和数据库的实现。
UserDetails是用户信息的抽象接口:
获取用户名。
获取密码。
获取权限,可以简单理解为角色名称(字符串),用于实现接口基于角色的授权访问,详情见后文。
获取用户是否可用,或用户/密码是否过期或锁定。
Spring Security 提供了一个 UserDetails 的实现类 User ,用于用户信息的实例表示。另外, User 提供 Builder 模式的对象构建方式。
设置用户名称。
设置密码,Spring Security 不建议使用明文字符串存储密码,密码格式:
其中,id 为加密算法标识,encodedPassword 为密码加密后的字符串。这里以加密算法 bcrypt 为例,详细内容可参考 Password Storage 。
设置角色,支持多个。
UserDetails实例创建完成之后,就可以使用 UserDetailsManager 的具体实现进行存储和读取。
InMemoryUserDetailsManager是 Spring Security 为我们提供的基于内存实现的 UserDetailsManager 。
使用 @Bean 将 InMemoryUserDetailsManager 实例注入 Spring Security。
创建 InMemoryUserDetailsManager 实例之后,并不是必须立即调用 createUser 添加用户信息,也可以在业务系统的其它地方获取已注入的 InMemoryUserDetailsManager 动态存储 UserDetails 实例。
编译启动应用,使用我们自己创建的用户名和密码(userA/123456)访问接口:
基于内存介质自定义的用户名和密码已生效,接口正常响应。
JdbcUserDetailsManager是 Spring Security 为我们提供的基于数据库实现的 UserDetailsManager ,相较于 InMemoryUserDetailsManager 使用略复杂,需要我们创建数据表,并准备好数据库连接需要的数据源(DataSource), JdbcUserDetailsManager 实例的创建依赖于数据源。
JdbcUserDetailsManager可以与业务系统共用一个数据库数据源实例,本文不讨论数据源的相关配置。
以 MySQL 为例,创建数据表语句:
其他数据库语句可参考 User Schema 。
JdbcUserDetailsManager实例的创建与注入,除
之外,整体流程与 InMemoryUserDetailsManager 类似,不再赘述。
在业务系统中获取已注入的 JdbcUserDetailsManager 实例,可以动态存储 UserDetails 实例。
编译启动应用,使用我们自己创建的用户名和密码(userA/123456)访问接口:
基于数据库介质自定义的用户名和密码已生效,接口正常响应。
Spring Security 可以提供基于角色的权限控制:
假设,存在两个角色 USER(普通用户) 和 ADMIN(管理员),
角色 USER 可以访问接口 /hello/name,
角色 ADMIN 可以访问接口 /hello/world,
所有用户认证后可以访问接口 /。
我们需要按上述需求重新设置 HttpSecurity :
设置角色 USER 可以访问接口 /hello/name。
设置角色 ADMIN 可以访问接口 /hello/world。
设置其他接口认证后即可访问。
mvcMatchers 支持使用通配符。
创建属于角色 USER 和 ADMIN 的用户:
用户名:userA,密码:123456,角色:USER
用户名:userB,密码:abcdef,角色:ADMIN
对于用户 userA :
使用用户 userA 的用户名和密码访问接口 /:
认证通过,可正常访问。
使用用户 userA 的用户名和密码访问接口 /hello/name:
认证通过,鉴权通过,可正常访问。
使用用户 userA 的用户名和密码访问接口 /hello/world:
认证通过,用户 userA 不属于角色 ADMIN,禁止访问。
使用用户 userA 的用户名和密码访问接口 /:
认证通过,可正常访问。
对于用户 userB :
使用用户 userB 的用户名和密码访问接口 /:
认证通过,可正常访问。
使用用户 userB 的用户名和密码访问接口 /hello/world:
认证通过,鉴权通过,可正常访问。
使用用户 userB 的用户名和密码访问接口 /hello/name:
认证通过,用户 userB 不属于角色 USER,禁止访问。
这里可能会有一点奇怪,一般情况下我们会认为 管理员 应该拥有 普通用户 的全部权限,即普通用户 可以访问接口 /hello/name,那么 管理员 应该也是可以访问接口 /hello/name 的。如何实现呢?
方式一,设置用户 userB 同时拥有角色 USER 和 ADMIN;
这种方式有点不够“优雅”。
方式二,设置角色 ADMIN 包含 USER;
Spring Security 有一个 Hierarchical Roles 的特性,可以支持角色之间的 包含 操作。
使用这个特性要特别注意两个地方:
前文使用的是 HttpSecurity.authorizeHttpRequests 方法,此处需要变更为 HttpSecurity.authorizeRequests 方法。
使用 RoleHierarchy 以 Bean 的方式定义角色之间的 层级关系 ;其中,“ROLE_” 是 Spring Security 要求的固定前缀。
编译启动应用,使用用户 userB 的用户名和密码访问接口 /hello/name:
认证通过,鉴权通过,可正常访问。
如果开启 Spring Security 的 debug 日志级别,访问接口时可以看到如下的日志输出:
可以看出,Spring Security 可以从角色 ADMIN 推导出用户实际拥有 USER 和 ADMIN 两个角色。
Hierarchical Roles 文档中的示例有明显错误:
接口 RoleHierarchy 中并不存在方法 setHierarchy 。前文所述 authorizeRequests 和 RoleHierarchy 结合使用的方法是结合网络搜索和自身实践得出的,仅供参考。
另外, authorizeHttpRequests 和 RoleHierarchy 结合是没有效果的, authorizeRequests 和 authorizeHttpRequests 两者之间的区别可以分别参考 Authorize HttpServletRequests with AuthorizationFilter 和 Authorize HttpServletRequest with FilterSecurityInterceptor 。
鉴权的前提需要认证通过;认证不通过的状态码为401,鉴权不通过的状态码为403,两者是不同的。
Spring Security 异常主要分为两种:认证失败异常和鉴权失败异常,发生异常时会分别使用相应的默认异常处理器进行处理,即:认证失败异常处理器和鉴权失败异常处理器。
使用的认证或鉴权实现机制不同,可能使用的默认异常处理器也不相同。
Spring Security 认证失败异常处理器:
如前文所述,认证失败时,Spring Security 使用默认的认证失败处理器实现返回:
如果想要自定义返回内容,则可以通过自定义认证失败处理器实现:
authenticationEntryPoint()会创建返回一个自定义的 AuthenticationEntryPoint 实例;其中,使用 HttpServletResponse.getWriter().print() 写入我们想要返回的内容:401。
httpBasic().authenticationEntryPoint(authenticationEntryPoint())使用我们自定义的 AuthenticationEntryPoint 替换 HttpBasic 默认的 BasicAuthenticationEntryPoint 。
编译启动应用,使用不正确的用户名和密码访问接口 /:
认证不通过,使用我们自定义的内容 401 返回。
Spring Security 鉴权失败异常处理器:
如前文所述,认证失败时,Spring Security 使用默认的认证失败处理器实现返回:
如果想要自定义返回内容,则可以通过自定义鉴权失败处理器实现:
自定义鉴权失败处理器与认证失败处理器过程类似,不再赘述。
编译启动应用,使用用户 userA 的用户名和密码访问接口 /hello/world:
鉴权不通过,使用我们自定义的内容 403 返回。
exceptionHandling()也是有一个 authenticationEntryPoint() 方法的;对于 HttpBasic 而言,使用 exceptionHandling().authenticationEntryPoint() 设置自定义认证失败处理器是不生效的,具体原因需要大家自行研究。
前文介绍两种认证方式: FormLogin 和 HttpBasic ,Spring Security 还提供其他若干种认证方式,详情可参考 Authentication Mechanisms 。
如果我们想实现自己的认证方式,也是比较简单的。Spring Security 本质就是 过滤器 ,我们可以实现自己的认证过滤器,然后加入到 Spring Security 中即可。
认证过滤器核心实现流程:
除去抛出异常的情况外, filterChain.doFilter(servletRequest, servletResponse); 是必须保证被执行的。
理解认证过滤器涉及的概念会比较多,详情参考 Servlet Authentication Architecture 。
认证过滤器创建完成之后,就可以加入到 Spring Security 中:
Spring Security 根据我们配置的不同,会为我们自动按照一定的次序组装一条 过滤器链 ,通过这条链上的若干过滤器完成认证鉴权的。我们需要把自定义的认证过滤器加到这个链的 合适位置 ,这是选取的位置是在 ExceptionTranslationFilter 的前面。
过滤器链的顺序可以参考 Security Filters 。
ExceptionTranslationFilter 的作用可以参考 Handling Security Exceptions 。
使用自定义认证过滤器时,自定义认证失败异常处理器和鉴权失败异常处理器的设置方法。
编译启动应用,我们会发现可以在不填入任何认证信息的情况下直接访问接口 / 和 /hello/name,因为模拟用户已认证且角色为 USER;访问接口 /hello/world 时会出现提示 403。
Spring Security 自身包含的内容很多,官方文档也不能很好的讲述清楚每个功能特性的使用方法,很多时候需要我们自己根据文档、示例、源码以及他人的分享,尽可能多的实践,逐步加深理解。

阅读更多 >>>  微信折叠群聊还能收到消息吗

用spring security完成权限管理和用基于JSP的权限的优缺点?

Spring
Security的权限控制主要是通过过滤器来实现,它的好处是可配置性和可扩展性都很强,至于基于JSP的权限,基于JSP的那叫hard
coding,那不叫权限设计方案,你的整个网站如果只有10个以内的页面可以这样耍耍,你的网站如果是面向生产的,我建议你还是采用专业的权限控制方案吧。

Spring Security系列之核心概念

提到安全,相信大家的第一反应都是加密,什么MD5、AES、SHA-256算法之类东西。没错加密各种算法是安全的基础。针对于基础的安全设施, Java Security 模块 提供了各种安全域的API、PKI、密码学及其内建实现、安全通信、认证、访问控制等丰富的功能,这些都是安全的基础。在Web应用的开发中,对于各种攻击防守,对于认证、授权的实现实现方案是极为重要的。Spring Security项目就是认证、授权、防攻击实现方案的集成框架。学习框架之前,必须要对安全领域的核心的概念进行梳理,这也是 Spring Security 本身所关注的点。

对请求资源的用户身份进行验证的过程,解决的是“这是谁请求的?”的问题。

确认当前用户是否有权限访问资源的过程,解决的是“他能不能做这个操作?”的问题。

跨站请求伪造(Cross Site Request Forgery)

请求是由外部网站伪造发送到目标服务服务器,而不是由用户主动发送请求至目标服务器这种伪造请求就是叫做CSRF。

同步令牌模式(Synchronizer Token Pattern) ,针对Post请求(避免get,会导致令牌泄漏),在请求参数中(一般是头部)增加同步令牌,服务器校验传入令牌的一致性判断是否是正确请求。请求令牌是从Cookie中获取的,由于同源策略外部站点无法获得令牌。能很好的解决该问题。

通常存在用户Session中。为什么不存在cookies中?这是一种早期处理方案,早期一些系统将token存在cookies中但是会出现了漏洞同 Ruby on Rails的CSRF保护绕过一样 ,同时系统失去了紧急情况下对于令牌的失效保护。web应用开发中关键信息存放在后台是一种比较安全的方式。令牌可能会随着Session过期而过期,可以通过主动取或者被动刷新的方式处理。

SameSite 是Http协议中的一个属性, 参考 。这是一种紧急的方案 ,实现防止CSRF攻击,可以在session cookie中设置如下不同的值:

针对cookie中存储了会话信息的请求如果丢失了cookie就会进入授权登录操作。使用SameSite 属性来防攻击要注意一些用户体验的场景,如Strict模式下邮件发送的地址就会失去了登录态,用户就需要再次登录可能会造成不好的体验。

从应用层面讲一般针对于浏览器用户,非浏览器场景就需要禁止CSRF Protection。对CSRF定义中它是依赖浏览器的,容易和中间人攻击混淆。

特别需要注意的具体场景如 login,logout,multipart是要重点进行CSRF保护。

在HTTP协议中有一些首部用于安全处理,本模块对Spring Security中关键安全首部进行讲解。其他参考: Security HTTP Response Headers , developer.mozilla.org

控制浏览器缓存用户浏览内容的首部。Spring Security 默认禁用缓存,可以避免敏感信息的泄露,如用户登录账号查看敏感信息后退出登录恶意用户通过浏览器回退查看到敏感信息。

针对Content-Type不存在的时候是否采用 内容嗅探(content sniffing) 的方式处理控制的首部。为了优化体验浏览器会自动判断响应内容的类型然后进行渲染。禁用sniff可以避免XSS攻击。因为 恶意用户可以创建一个postscript的js文档并用它来执行XSS攻击 。

Http严格传输安全,HTST(Http Strict Transport Security)。很多用户习惯输入不带https协议的域名,中间人可能会拦截到http并窃取https的响应给用户造成中间人攻击。Strict-Transport-Security首部将指导浏览器将其设置为严格的安全传输地址。 参考RFC6797

Http公钥固定(HPKP),用来告诉Web客户端将特定的加密公钥于某个Web服务器相关联,以降低使用证书伪造的MITM攻击的风险。 已被废弃 。

是否允许自己的网站被嵌入到其他网站。用来避免点击劫持(Click Jacking)

针对检测到跨域脚本攻击的中止操作,现代化的浏览器可以通过 Content-Security-Policy 针对不支持CSP的浏览器该保护的是很有效的。该首部没有被全力支持,只有少部分浏览器是支持的如:IE,Safari。

CSP是作为一种向客户端传递安全策略的机制,每一种安全策略代表了一组安全指令,每个指令对资源的呈现做了限制。 Content-Security-Policy , Content-Security-Policy-Report-Only

Referrer 首部标记的是资源最初来源, Referrer-Policy 主要就是控制多少引用信息包含在请求中。

为开发者提供了选择性禁用、启用、修改固定API或者浏览器功能的机制。

提供了通过首部清除浏览器侧数据的方式,例如登出的时候设置首部来清除浏览器侧的相关数据。

网站数据信息

"springsecurity权限控制,Springboot security oauth2 jwt实现权限控制,实现微服务获取当前用户信息"浏览人数已经达到21次,如你需要查询该站的相关权重信息,可以点击进入"Chinaz数据" 查询。更多网站价值评估因素如:springsecurity权限控制,Springboot security oauth2 jwt实现权限控制,实现微服务获取当前用户信息的访问速度、搜索引擎收录以及索引量、用户体验等。 要评估一个站的价值,最主要还是需要根据您自身的需求,如网站IP、PV、跳出率等!