具有PreAuthorize的Spring方法安全性

朋友不允许朋友写用户身份验证。 厌倦了管理自己的用户? 立即尝试Okta的API和Java SDK。 在几分钟之内即可对任何应用程序中的用户进行身份验证,管理和保护。

本教程将探讨使用Spring Security在Spring Boot中配置身份验证和授权的两种方法。 一种方法是创建WebSecurityConfigurerAdapter并使用流畅的API覆盖HttpSecurity对象上的默认设置。 另一个方法是在控制器方法上使用@PreAuthorize批注,称为方法级安全性或基于表达式的安全性。 后者将是本教程的重点。 但是,我将通过对比介绍一些HttpSecurity代码和想法。

第一种身份验证方法是HttpSecurity ,它是全局的,默认情况下应用于所有请求。 但是,可以通过对端点使用模式匹配来进行更细粒度的控制,并且HttpSecurity公开的流畅API十分强大。 在此处公开了OAuth 2.0,表单登录和HTTP Basic等配置选项。 这是设置全局身份验证策略的好地方。

方法级别的安全性是通过将@PreAuthorize批注放置在控制器方法上(实际上是一组可用的批注之一,但最常用)来实现。 该批注包含一个Spring Expression Language(SpEL)代码段,该代码段经过评估以确定是否应该对请求进行身份验证。 如果未授予访问权限,则不会执行该方法,并返回HTTP Unauthorized。 实际上,在控制器方法上使用@PreAuthorize注释与在特定端点上使用HttpSecurity模式匹配器非常相似。 但是有一些区别。

区分Spring Security的@PreAuthorize和HttpSecurity

第一个区别是微妙的,但值得一提。 在发生控制器映射之前, HttpSecurity方法会在Web请求过滤器中更早地拒绝该请求。 相反, @PreAuthorize评估是在控制器方法执行之前@PreAuthorize进行的。 这意味着在HttpSecurity 之前应用 @PreAuthorize中的配置。

其次, HttpSecurity绑定到URL端点,而@PreAuthorize绑定到控制器方法,并且实际上位于与控制器定义相邻的代码内 。 将所有安全性集中在一处并由Web终结点定义具有一定的整洁性,尤其是在较小的项目中,或者在更全局的设置中; 但是,随着项目的扩大,使授权策略靠近受保护的代码可能更有意义,这就是基于注释的方法所允许的。

另一个优点是@PreAuthorize呈现在HttpSecurity就是利用规划环境地政司的。 使用Spring Expression Language,您可以基于复杂的表达式来做出授权决策,这些复杂的表达式可以访问内置的身份验证对象(例如authenticationprincipal ),依赖项注入的方法参数和查询参数。 在本教程中,您将主要看两个表达式: hasAuthority()hasRole() 。 Spring文档再次是深入研究的好地方。

在深入研究该项目之前,我还要提到Spring还提供了@PostAuthorize批注。 毫不奇怪,这是在方法执行评估的方法级授权注释。 我们为什么要这样做? 它允许方法在评估注释之前根据喜欢的控制器逻辑执行自己的授权检查。 缺点是,由于控制器方法是在评估注释之前执行的,因此可能会导致效率低下,具体取决于实现方式。

依存关系

本教程的依赖项非常简单。 您需要:1)已安装Java 8+,以及2)Okta开发人员帐户。

如果没有安装Java,请转到AdoptOpenJDK 。 在* nix系统上,您也可以使用SDKMAN 。

如果您还没有免费的Okta开发者帐户,请访问我们的网站并注册 。

使用Spring Initializr启动一个示例项目

要启动项目,可以使用Spring Initializr 。 但是,尽管值得一游,但您甚至不必费心去那里创建项目。 您可以使用REST API和curl

打开终端,然后将cd转到您希望项目文件.zip结束的任何位置。 运行以下命令,该命令将下载压缩的Spring Boot项目。

curl https://start.spring.io/starter.zip \-d dependencies=web,security \-d type=gradle-project \-d bootVersion=2.1.5.RELEASE \-d groupId=com.okta.preauthorize \-d artifactId=application \-o PreAuthorizeProject.zip
unzip PreAuthorizeProject.zip

除了build.gradle文件和DemoApplication.java类文件之外,该项目没有多少其他内容。 但是,已经为您设置了整个项目结构。

对于本示例, build.gradle文件还具有两个Spring依赖项:

dependencies {implementation 'org.springframework.boot:spring-boot-starter-security'implementation 'org.springframework.boot:spring-boot-starter-web'
}

添加一个WebController

当前状态下的示例应用程序并没有做太多事情。 您需要添加一个控制器来定义一些端点和响应。

添加一个新文件src/main/java/com/okta/preauthorize/application/WebController.java

package com.okta.preauthorize.application;  import org.springframework.stereotype.Controller;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.bind.annotation.ResponseBody;  @Controller  
public class WebController {  @RequestMapping("/")  @ResponseBody  public String home() {  return "Welcome home!";  }  @RequestMapping("/restricted")  @ResponseBody  public String restricted() {  return "You found the secret lair!";  }
}

该控制器定义两个端点: //restricted 。 您将稍后向/restricted端点添加方法级安全性。 但是,目前尚未配置安全性。

继续运行该应用程序。 在根项目目录中,运行:

./gradlew bootRun

Spring Boot完成启动后,导航至http://localhost:8080

您会注意到出现了一个登录表单。 哇! 那个是从哪里来的?!

该表单是由Spring Boot自动生成的。 看一下Spring类WebSecurityConfigurerAdapter和方法configure(HttpSecurity http) 。 这是配置默认身份验证设置的位置。

/**  * Override this method to configure the {@link HttpSecurity}. Typically subclasses  * should not invoke this method by calling super as it may override their * configuration. The default configuration is: 
* 
*
  
* http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic(); 
* 
* * @param http the {@link HttpSecurity} to modify * @throws Exception if an error occurs */ protected void configure(HttpSecurity http) throws Exception { ... http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin().and() .httpBasic(); }

默认情况下,所有请求都需要身份验证。 为所有请求启用基于表单的身份验证和HTTP Basic身份验证。

如果要登录,请在控制台输出中查找类似于以下内容的行,以告诉您所生成的密码:

Using generated security password: 9c299bb9-f561-4c12-9810-c9a2bc1dca08

用户名就是“用户”。 每次运行Spring Boot时都会重新生成此密码。

身份验证与授权

在继续之前,我想快速确保两个术语明确:身份验证和授权。 身份验证回答问题:谁在发出请求? 授权回答了这个问题:他们被允许做什么?

通常,首先进行身份验证,除非为匿名用户设置了特定的权限(在某些方面这是隐式身份验证)。 授权基于经过身份验证的用户的价值。 认证实体可以是人类用户或自动化服务,也可以是代表人类用户运行的服务。

两种常见的授权方案基于组和角色。 这两个术语通常在网络上不太知名的地方被混用并互换使用,但是在官方上有区别。 组定义用户组,并向这些用户组分配权限。 用户可以是多个组的成员。 角色定义可以分配给用户的权限集(允许的操作或资源)。 在实践中,组往往是一种更静态,更不灵活的控制器访问方式,而角色通常是精细粒度的,甚至在会话中也可以是动态的,为特定任务分配角色,并在不再需要它们时将其撤销。 使用Amazon AWS的任何人都已经看到了这种做法,通常会产生令人困惑的效果。

为Spring @PreAuthorize启用方法级安全性

您现在想要做的是配置Spring Boot以允许home端点上的请求,同时将请求限制为/restricted端点。

您最初可能以为可以将@PreAuthorize("permitAll()")到本地端点,这将允许所有请求。 但是,如果尝试使用它,您会发现它什么也没做。 这是因为默认的HttpBuilder实现仍处于活动状态,并且因为它是在Web请求过滤器链中进行评估的,因此它具有优先权。 您还必须显式启用方法级别的安全注释,否则,它们将被忽略。

添加以下SecurityConfig类,将实现上述两个目标。

src/main/java/com/okta/preauthorize/application/SecurityConfig.java

package com.okta.preauthorize.application;  import org.springframework.context.annotation.Configuration;  
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;  
import org.springframework.security.config.annotation.web.builders.HttpSecurity;  
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;  @Configuration  
@EnableGlobalMethodSecurity(prePostEnabled = true)  
public class SecurityConfig extends WebSecurityConfigurerAdapter {  protected void configure(final HttpSecurity http) throws Exception {}  
}

@EnableGlobalMethodSecurity(prePostEnabled = true)注释启用了@PreAuthorize注释。 可以使用@Configuration批注将其添加到任何类。 在这里,我将不做任何深入的介绍,但是您也可以启用@Secured ,较旧的Spring Security注释和JSR-250注释。

configure(final HttpSecurity http)方法将覆盖默认的HttpBuilder配置。 因为它是空的,所以它使应用程序未经授权或认证。

使用./gradlew bootRun再次运行该应用程序,您会发现两个端点都已完全打开。

实施全球安全政策

应用程序通常必须选择要围绕其构建安全性的全局安全策略:“默认为允许”或“默认为已验证”。 该应用程序默认情况下是否打开? 还是默认情况下受限制? 我通常默认为受限,并明确允许任何公共端点。 对于我所使用的专有Web应用程序类型,这些类型通常不是公开的,或者具有相对较小的公开面Kong,因此该方案有意义。 但是,如果您要进行的工作大部分是公开的,并且要有离散的访问控制支持,例如带有管理面板的网站,那么可能会采用一种更为宽松的方案。 您将在此处查看两者。

由于该应用已经开放,因此我将首先向您展示如何限制特定方法。 在那之后,您将研究几种方法来实施更具全局限制的访问策略。

WebController类中,将@PreAuthorize批注添加到/restricted端点,如下所示:

@PreAuthorize("isAuthenticated()")  
@RequestMapping("/restricted")  
@ResponseBody  
public String restricted() {  return "You found the secret lair!";  
}

运行应用程序( ./gradlew bootRun )。

这次,您将能够导航到主页,但是转到/restricted端点将为您提供(非常难看的)whitelabel错误页面。

在生产应用程序中,您需要重写此代码以返回更好的自定义错误页面,或者以其他方式处理错误(如果要创建自定义错误页面,请查看主题的Spring文档 )。 但是您可以看到该应用返回的是403 /未经授权 ,这就是您想要的。

大。 现在,改为更改您的WebController类以匹配以下内容:

@Controller
@PreAuthorize("isAuthenticated()")    
public class WebController {  @PreAuthorize("permitAll()")  @RequestMapping("/")  @ResponseBody  public String home() {  return "Welcome home!";  }  @RequestMapping("/restricted")  @ResponseBody  public String restricted() {  return "You found the secret lair!";  }  }

在这里,您已使用@PreAuthorize批注将整个控制器类限制为经过身份验证的用户,并明确允许所有请求(无论身份验证状态如何)到本地端点。

我知道我们一直将其称为“方法级”安全性,但实际上,也可以将这些@PreAuthorize批注添加到控制器类中,以为整个类设置默认值。 这也是@PreAuthorize("permitAll()")派上用场的地方,因为它可以覆盖类级别的注释。

如果运行该应用程序( ./gradlew bootRun )并尝试使用端点,则会发现home端点处于打开状态,而/restricted端点处于关闭状态。

请注意,如果添加了另一个单独的Web控制器,则默认情况下,其所有方法仍将处于打开状态,并且不需要身份验证。

第三种选择(我最喜欢的是大多数中小型应用程序)是使用HttpBuilder在默认情况下要求对所有请求进行身份验证并显式允许任何公共端点。 这使您可以使用@PreAuthorize来基于用户或角色或组来优化对特定方法的访问控制,但要明确指出, 除非明确允许否则所有路径都将应用一些基本安全性。 这也意味着公共路径定义在中心位置。 同样,这适用于某种类型的项目,但可能不是所有项目的最佳结构。

要实现此目的,请将WebController类更改为此(删除所有@PreAuthorize批注):

@Controller  
public class WebController {  @RequestMapping("/")  @ResponseBody  public String home() {  return "Welcome home!";  }  @RequestMapping("/restricted")  @ResponseBody  public String restricted() {  return "You found the secret lair!";  }  }

并将WebSecurity类更改为:

Configuration  
@EnableGlobalMethodSecurity(prePostEnabled = true)  
public class SecurityConfig extends WebSecurityConfigurerAdapter {  protected void configure(final HttpSecurity http) throws Exception {  http.antMatcher("/**")  .authorizeRequests()  .antMatchers("/").permitAll()  .anyRequest().authenticated().and().formLogin();  }  
}

您要做的是使用SecurityConfig类显式允许本地终结点计算机上的所有请求,同时要求在所有其他终结点计算机上进行身份验证。 这为您的应用设置了全面的最低身份验证要求。 您还重新启用了基于表单的身份验证。

尝试一下!

使用以下./gradlew bootRun运行应用程序: ./gradlew bootRun

导航到打开的home端点: http://localhost:8080

以及需要认证的受限端点: http://localhost:8080/restricted

当Spring的登录表单出现时,别忘了您可以使用默认凭据。 用户是“用户”,并且在控制台输出中找到了密码(查找Using generated security password:

前进到OAuth 2.0登录

这些天,基于表单的身份验证感觉非常破旧。 用户越来越希望能够使用第三方站点登录,由于安全威胁的增加,在您自己的服务器上管理用户凭据的动机也越来越少。 Okta是一家提供服务的软件即服务身份和访问管理公司。 在本部分中,您将使用它们来使用OAuth 2.0和OIDC(OpenID Connect)快速实现登录表单。

非常非常简短:OAuth 2.0是一种行业标准的授权协议,而OIDC是OAuth之上的另一个开放标准,它添加了一个身份层(身份验证)。 它们共同为程序提供了一种结构化的方式来管理身份验证和授权,以及跨网络和Internet进行通信。 但是,OAuth和OIDC均未提供实现。 它们只是规格或协议。 这就是Okta的用处。Okta实现了OAuth 2.0和OIDC规范的实现,该规范允许程序使用其服务来快速提供登录,注册和单点登录(或社交登录)服务。 在本教程中,您将仅实现登录功能,但是在本教程的最后,您可以找到指向其他资源的链接,以向您展示如何实现社交登录和注册。

首先,注册一个免费的Okta Developer帐户: https : //developer.okta.com/signup/ 。

如果这是您首次登录或刚刚注册,则可能需要单击Admin按钮才能进入开发人员控制台。

接下来,您需要配置OIDC应用程序。

在Okta开发人员仪表板的顶部菜单中,单击应用程序

  • 单击绿色的“ 添加应用程序”按钮
  • 单击“ Web应用程序类型”,然后单击“下一步”。
  • 为应用命名。 任何名字。
  • 登录重定向URI设置为http://localhost:8080/login/oauth2/code/okta
  • 单击完成

记下页面底部的客户端ID客户端密钥 。 在下一部分中,您将需要这些。

在Okta方面就是这样。

现在,您需要配置Spring Boot以将Okta用作OAuth 2.0提供程序。

为OAuth 2.0配置Spring Boot应用

使用OAuth 2.0和Okta使Spring Boot变得非常容易。 第一步是添加Okta Spring Boot Starter依赖项。 无需使用我们的启动程序就可以完全使用Okta OAuth 2.0 / OIDC; 但是,启动器简化了配置。 它还处理从JSON Web令牌中提取组声明,并将其转换为Spring Security授权(稍后会介绍)。 查看Okta Spring Boot Starter GitHub页面以获取更多信息。

更新build.gradle文件的依赖项部分:

dependencies {  implementation 'com.okta.spring:okta-spring-boot-starter:1.2.1'  // <-- ADDEDimplementation 'org.springframework.boot:spring-boot-starter-security'  implementation 'org.springframework.boot:spring-boot-starter-web'  testImplementation 'org.springframework.boot:spring-boot-starter-test'  testImplementation 'org.springframework.security:spring-security-test'  
}

src/main/resources目录中,有一个application.properties文件。 将其重命名为application.yml 。 添加以下内容:

okta:oauth2:  issuer: https://{yourOktaDomain}/oauth2/default  client-id: {yourClientId}client-secret: {yourClientSecret}

不要忘记更新client-idclient-secretissuer值以匹配Okta开发人员帐户和OIDC应用程序中的值。 Okta发行者的外观应类似于https://dev-123456.okta.com/oauth2/default

最后,更新SecurityConfiguration.java文件:

@Configuration  
@EnableGlobalMethodSecurity(prePostEnabled = true)  
public class SecurityConfig extends WebSecurityConfigurerAdapter {  protected void configure(final HttpSecurity http) throws Exception {  http.antMatcher("/**")  .authorizeRequests()  .antMatchers("/").permitAll()  .anyRequest().authenticated()  .and().oauth2Login();  // <-- THIS WAS CHANGED}  
}

请注意,您在这里真正进行的更改只是从formLogin()oauth2Login()

运行应用程序: ./gradlew bootRun (您可能需要退出Okta开发人员仪表板,或使用隐身窗口查看登录屏幕)。

/端点仍然打开,但是当您访问受限端点时: http://localhost:8080/restricted

您将看到Okta登录屏幕。

使用您的Okta凭据登录,即可通过身份验证!

检查OAuth 2.0用户属性

开发OAuth应用程序时,我发现能够检查Spring Boot关于客户端和已认证用户的信息会很有帮助。 为此,添加一个名为UserInfoController.java的新控制器。

src/main/java/com/okta/preauthorize/application/UserInfoController.java

package com.okta.preauthorize.application;  import org.springframework.security.core.annotation.AuthenticationPrincipal;  
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;  
import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;  
import org.springframework.security.oauth2.core.user.OAuth2User;  
import org.springframework.stereotype.Controller;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.bind.annotation.ResponseBody;  import java.util.Map;  @RequestMapping("/user")  
@Controller  
public class UserInfoController {  @RequestMapping("/oauthinfo")  @ResponseBody  public String oauthUserInfo(@RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient,  @AuthenticationPrincipal OAuth2User oauth2User) {  return  "User Name: " + oauth2User.getName() + "<br/>" +  "User Authorities: " + oauth2User.getAuthorities() + "<br/>" +  "Client Name: " + authorizedClient.getClientRegistration().getClientName() + "<br/>" +  this.prettyPrintAttributes(oauth2User.getAttributes());  }  private String prettyPrintAttributes(Map<String, Object> attributes) {  String acc = "User Attributes: <br/><div style='padding-left:20px'>";  for (String key : attributes.keySet()){  Object value = attributes.get(key);  acc += "<div>"+key + ":&nbsp" + value.toString() + "</div>";  }  return acc + "</div>";  }  
}

请注意,该类定义了全类的请求映射路径/user ,从而形成了实际的oauthUserInfo()端点/user/oauthinfo

第二种方法prettyPrintAttributes()只是一些用来格式化用户属性的糖,使它们更具可读性。

运行应用程序: ./gradlew bootRun

导航到http://localhost:8080/user/oauthinfo

您会看到以下内容:

User Name: 00ab834zk7eJ18e8Y0h7  
User Authorities: [ROLE_USER, SCOPE_email, SCOPE_openid, SCOPE_profile]  
Client Name: Okta  
User Attributes:  at_hash: 1yq0lbHDupcb8AhBNShkeQsub: 00ue9mlzk7eW24e8Y0h7zoneinfo: America/Los_Angelesver: 1email_verified: trueamr: ["pwd"]iss: https://dev-123456.oktapreview.com/oauth2/defaultpreferred_username: andrew.hughes@mail.comlocale: en-USgiven_name: Andrewaud: [0oakz4teswoV7sDZI0h7]updated_at: 1558380884idp: 00oe9mlzh0xuqOT5z0h7auth_time: 1558454889name: Andrew Hughesexp: 2019-05-21T17:46:28Zfamily_name: Hughesiat: 2019-05-21T16:46:28Zemail: andrew.hughes@mail.comjti: ID.CnwVJ_h1Dq5unqkwherWyf8ZFTETX_X4TP39ythQ-ZE

我想指出几件事。 首先,请注意, 用户名实际上是Okta的OIDC应用程序的客户端ID。 这是因为,从Spring Boot OAuth的角度来看,客户端应用程序是“用户”。 要找到实际的用户名和信息,您必须在User Attributes查看 。 请记住,这些属性的实际内容在OAuth提供程序之间会有所不同,因此,例如,如果您支持Okta,GitHub和Twitter,那么您将需要检查每个OAuth提供程序的这些属性以查看它们返回的内容。

另一个要点是用户权限 。 如Spring在这里所使用的,授权机构是授权信息的元术语。 它们只是字符串。 它们的值是从OAuth / OIDC信息中提取的。 客户端应用程序可以正确使用它们。 它们可能是角色,作用域,组等。就OAuth / OIDC而言,它们的用法基本上是任意的。

要查看其工作原理,在接下来的几节中,您将在Okta中添加一个Admin组,将用户分配给该组,然后使用@PreAuthorize批注将方法限制为Admin组。

ROLE_USER :您会注意到,Spring已为所有经过身份验证的用户分配了ROLE_USER 。 这是自动分配的默认最低级别角色。

SCOPE_ :还应注意,OAuth范围已映射到Spring授权,并且可以用于授权,例如,在@PreAuthorize@PostAuthorize批注中,正如您将在@PostAuthorize秒钟后看到的那样。

激活对Okta的团体声明

Okta默认情况下不将组声明包括在JSON Web令牌(JWT)中。 JWT是Okta用于将身份验证和授权信息传达到客户端应用程序的工具。 在本文末尾链接的其他一些博客文章中,可以找到更深入的了解。

要将Okta配置为添加组声明,请转到Okta开发人员仪表板。

在顶部菜单中,转到API并选择授权服务器

选择默认授权服务器。

单击“ 索赔”选项卡。

您将创建两个索赔映射。 您本身并不是在创建两个声明,而是指示Okta将组声明添加到访问令牌和ID令牌中。 您需要执行此操作,因为根据OAuth流的不同,可能会从任一组中提取组声明。 在我们的案例中,对于OIDC流程而言,实际上实际上是ID令牌,但是最好将它们同时添加到两者中,以免日后沮丧。 资源服务器流要求组声明位于访问令牌中。

首先,为令牌类型Access Token添加一个声明映射。

点击添加声明

更新以下值(其他默认值也可以):

  • 名称:团体
  • 包含在令牌类型中 :访问令牌
  • 值类型:
  • 过滤器:匹配正则表达式.*

其次,为令牌类型ID Token添加第二个声明映射。

点击添加声明

更新以下值(除了令牌类型外,与上面的值相同):

  • 名称:团体
  • 包含在令牌类型中 :ID令牌
  • 值类型:
  • 过滤器:匹配正则表达式.*

大! 因此,现在Okta会将其所有组映射到有关访问令牌和ID令牌的groups声明。

在Spring,这些团体声称发生的事情不一定是显而易见的,也不是自动的。 Spring Boot启动程序的好处之一是,它会自动从JWT中提取组声明并将其映射到Spring授权机构。 否则,您将需要实现自己的GrantedAuthoritiesExtractor

仅供参考:可以使用application.yml文件中的okta.oauth2.groupsClaim字段配置组声明的名称。 它默认为groups

使用组检查用户属性

运行应用程序: ./gradlew bootRun

导航到http://localhost:8080/user/oauthinfo

您将看到类似这样的内容(为清楚起见,省略了许多冗余行):

User Name: 00ab834zk7eJ18e8Y0h7  
User Authorities: [Everyone, ROLE_USER, SCOPE_email, SCOPE_openid, SCOPE_profile]
Client Name: Okta  
User Attributes:  ...groups: ["Everyone"]...

注意一个新的用户属性(映射的组声明)。 值Everyone是映射到Everyone的默认组。 这被映射到用户权限Everyone

这是基本思想。 添加管理员组后,下一步将更加令人兴奋。

在Okta中创建一个管理员组

现在,您想在Okta上添加一个管理员组。 登录到Okta开发人员仪表板。

在顶部菜单中,转到“ 用户”,然后选择“ 组”

单击添加组

在弹出窗口中:

  • 将组命名为“ Admin”。
  • 描述可以是您喜欢的任何内容。
  • 单击添加组

至此,您已经创建了Admin组, 但实际上尚未分配任何人!

使用方法级授权来限制端点

WebController类中,更新/restricted终结点方法。 您将在方法中添加以下注释:

@PreAuthorize("hasAuthority('Admin')")

像这样:

@PreAuthorize("hasAuthority('Admin')")  
@RequestMapping("/restricted")  
@ResponseBody  
public String restricted() {  return "You found the secret lair!";  
}

这告诉Spring检查经过身份验证的用户是否具有Admin权限,如果没有,则拒绝该请求。

运行应用程序: ./gradlew bootRun

导航到http://localhost:8080/restricted

您将收到403 /未经授权的白页错误。

将您的用户添加到管理员组

现在,您需要将Okta用户添加到Admin组。 从顶部菜单中,选择“ 用户” ,然后单击“ 组” 。 单击管理员组。 单击添加成员 。 在弹出窗口中搜索您的用户,然后单击添加

测试管理员组成员身份

做完了! 让我们看看这样做了。 再次运行Spring Boot应用程序: ./gradlew bootRun

导航到http://localhost/user/oauthinfo

这次,您将看到Admin组和权限。 (您可能需要一个新的浏览器会话才能看到更改,或使用隐身模式。)

User Name: 00ab834zk7eJ18e8Y0h7  
User Authorities: [Admin, Everyone, ROLE_USER, SCOPE_email, SCOPE_openid, SCOPE_profile]
Client Name: Okta  
User Attributes:  ...groups: ["Everyone", "Admin"]...

并且,如果您导航到http://localhost:8080/restricted ,则将被允许访问。

比较Spring Security角色和权限

最初让我感到困惑的一件事是hasRole()hasAuthority()

Spring中的角色是具有ROLE_前缀的授权机构(就像Spring中的所有其他事物一样,该前缀是可配置的)。 考虑这种情况的一种方法是,角色应用于大量权限,而权限可用于更精细的控制。 但是,这只是一种可能的用法。 实际的实现取决于开发人员。 在本教程中,您实际上是在使用权限映射到授权组。

要记住的重要一点是,如果要使用hasRole() ,则需要声明中的授权名称以ROLE_ 。 例如,如果您添加了一个ROLE_ADMIN组,并将您的用户添加到该组,并将该组添加到OIDC应用程序,则可以使用hasRole('ADMIN')

基于OAuth 2.0范围的授权和Spring PreAuthorize

您还可以使用@PreAuthorize批注来限制基于OAuth范围的访问。 从OAuth 2.0范围文档中 :

范围是OAuth 2.0中的一种机制,用于限制应用程序对用户帐户的访问。 一个应用程序可以请求一个或多个范围,然后在同意屏幕中将此信息呈现给用户,并且颁发给该应用程序的访问令牌将限于所授予的范围。

如果查看从/user/oauthinfo端点返回的经过检查的User Authorities ,则会看到三个以SCOPE_开头的SCOPE_

  • SCOPE_email
  • SCOPE_openid
  • SCOPE_profile

这些对应于电子邮件,openid和配置文件范围。 要将方法限制为具有特定范围的用户,应使用诸如: @PreAuthorize("hasAuthority('SCOPE_email')")的注释。

我还将指出,您可以使用SecurityConfig类中的HttpSecurity完成完全一样的事情(或多或少完全一样):

protected void configure(final HttpSecurity http) throws Exception {  http.antMatcher("/**")  .authorizeRequests()  .antMatchers("/").permitAll()  .antMatchers("/restricted").hasAuthority("SCOPE_custom") // <- LOOK AT ME!  .anyRequest().authenticated()  .and().oauth2Login();  
}

您可以通过将scopes属性添加到application.yml文件来定制客户端应用程序从Okta授权服务器请求的scopes 。 例如,在下面,我将application.yml文件设置为仅请求openid范围,这对于OAuth是必需的。

okta:  oauth2:  ... scopes: openid...

如果在/user/oauthinfo端点上运行此请求,则会得到以下信息:

User Name: 00ab834zk7eJ18e8Y0h7  
User Authorities: [Admin, Everyone, ROLE_USER, SCOPE_openid]  
Client Name: Okta  
...

请注意,只有一个范围已分配给用户权限。

尝试添加自定义范围。 更改application.yml文件中的okta.oauth2.scopes属性以使其匹配:

okta:  oauth2:  ... scopes: openid email profile custom...

在运行应用程序并尝试之前,您需要将自定义范围添加到Okta授权服务器(如果现在运行它,将会出现错误)。

打开您的Okta开发人员仪表板。

在顶部菜单中,转到API并选择授权服务器

选择默认授权服务器。

单击“ 作用域”选项卡。

单击添加范围按钮。

  • 名称custom
  • 描述Custom test scope

点击创建

您刚刚向默认的Okta授权服务器添加了一个自定义范围(俗称为custom )。

最后一次,运行Spring Boot应用程序: ./gradlew bootRun

导航到http://localhost:8080/user/oauthinfo

User Name: 00ab834zk7eJ18e8Y0h7  
User Authorities: [Admin, Everyone, ROLE_USER, SCOPE_custom, SCOPE_email, SCOPE_openid, SCOPE_profile]  
Client Name: Okta  
...

成功! 您应该在用户权限中看到新的SCOPE_custom

Spring Boot中的Spring PreAuthorize,HttpSecurity和Security

您覆盖了一大堆土地! 您使用@PreAuthorize很好地了解了Spring方法级别的安全性,并了解了它与HttpSecurity 。 您使用了一些基本的SpEL(Spring表达式语言)语句来配置授权。 您检查了授权和身份验证之间的区别。 您将Spring Boot配置为使用Okta作为OAuth 2.0 / OIDC单一登录提供程序,并向身份验证服务器和客户端应用添加了组声明。 您甚至还创建了一个新的Admin组,并了解了如何使用映射到Spring授权的组声明来限制访问。 最后,您了解了OAuth 2.0范围如何用于定义授权方案并在应用程序中实现它们。

下一站:火箭科学!

如果您想查看这个完整的项目,可以在GithHub上找到该仓库 。

如果您想了解有关Spring Boot,Spring Security或安全用户管理的更多信息,请查看以下任何出色的教程:

  • Spring Boot,OAuth 2.0和Okta入门
  • 15分钟内将单一登录添加到您的Spring Boot Web App
  • 使用多重身份验证保护您的Spring Boot应用程序安全
  • 使用Spring Boot和GraphQL构建安全的API

如果您想更深入地学习,请查看Okta Spring Boot Starter GitHub Project 。

如果您对此帖子有任何疑问,请在下面添加评论。 有关更多精彩内容, 请在Twitter上关注@oktadev , 在Facebook上关注我们,或订阅我们的YouTube频道 。

“带有PreAuthorize的Spring方法安全性”最初于2019年6月20日发布在Okta Developer博客上。

朋友不允许朋友写用户身份验证。 厌倦了管理自己的用户? 立即尝试Okta的API和Java SDK。 在几分钟之内即可对任何应用程序中的用户进行身份验证,管理和保护。

翻译自: https://www.javacodegeeks.com/2019/09/spring-method-security-preauthorize.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/341975.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数字音频光端机的工作原理及其应用介绍

数字音频光端机在通信系统中可以说是应用非常广泛&#xff0c;特别是在安防系统、智能交通监控系统、远程多媒体教学、校园监控、长距离广播电视传输系统、楼宇控制系统等&#xff0c;可以说我们的日常通讯是离不开音频光端机的。接下来就由飞畅科技为大家详细介绍下数字音频光…

[渝粤教育] 湘潭大学 土力学 参考 资料

教育 -土力学-章节资料考试资料-湘潭大学【】 1-1 土的形成随堂测验 1、【单选题】长江三角洲位于从长江入海口&#xff0c;该地的土主要为&#xff08; &#xff09;。 A、坡积土 B、冲积土 C、风积土 D、冰碛土 参考资料【 】 2、【填空题】岩石的风化可分为物理风化和 。 A、…

网络光端机产品特点及实际应用范围详解

在远程光纤传输中&#xff0c;光缆对信号的传输影响很小&#xff0c;光纤传输系统的传输质量主要取决于光端机的质量&#xff0c;因为光端机负责光电转换以及光发射和光接收&#xff0c;它的优劣直接影响整个系统&#xff0c;所以就需要众多新用户或对此有意向的用户对光端机的…

[渝粤教育] 盐城工学院 水处理微生物学 参考 资料

教育 -水处理微生物学-章节资料考试资料-盐城工学院【】 第1章 绪论-单元作业 第2章 微生物的纯培养和显微技术-单元作业1 第2章 微生物的纯培养和显微技术-单元作业2 第1章第2章-单元测验1 1、【单选题】最关键、最本质的微生物特点是&#xff08; &#xff09;。 A、体积小、…

几种常见光端机的作用介绍

可能很多人对于光端机这种设备比较陌生&#xff0c;有些就算是知道但也了解的不多&#xff0c;其实光端机就是一个延长数据传输的光纤通信设备&#xff0c;它通过信号调制、光电转化等技术&#xff0c;来达到远程传输的目的。今天&#xff0c;杭州飞畅的小编来告诉大家一些常见…

音频光端机的几个重要指标

音频光端机是一种音频设备&#xff0c;发射端把传统的音频模拟信号转换成光信号&#xff0c;通过光纤传输到接收端&#xff0c;在接收端再转换成模拟信号。今天&#xff0c;飞畅科技的小编就来为大家详细介绍下音频光端机的几个重要指标&#xff0c;感兴趣的朋友就一起来看看吧…

[渝粤教育] 西北大学 仪器分析 参考 资料

教育 -仪器分析-章节资料考试资料-西北大学【】 第一讲 光分析方法的基本概念单元测验题 1、【单选题】光谱分析法是一种&#xff08; &#xff09;来确定物质的组成和结构的仪器分析方法。 A、利用物质与光相互作用的信息 B、利用光的波动性 C、利用光的粒子性 D、利用物质的折…

音频光端机的选择

音频光端机就是发射端把传统的音频模拟信号转换成光信号&#xff0c;通过光纤传输到接收端&#xff0c;在接收端再转换成模拟信号的一种音频设备。用户在选择音频光端机的时候一般情况下是从它的相关参数和实用性方面来考虑&#xff0c;相关的技术参数在光端机出厂说明书中都有…

音频光端机简单故障处理

音频光端机是在通信系统中应用比较广泛的一种设备&#xff0c;目前在高速公路、交通、电子警察、监控、安防、工业自动化、电力、海关、水利、银行等领域&#xff0c;视频图像、音频、数据、以太网等光端机已开始普遍大量应用。但是&#xff0c;我们在使用音频光端机的过程中难…

q7goodies事例_Java 8 Friday Goodies:SQL ResultSet流

q7goodies事例在Data Geekery &#xff0c;我们喜欢Java。 而且&#xff0c;由于我们真的很喜欢jOOQ的流畅的API和查询DSL &#xff0c;我们对Java 8将为我们的生态系统带来什么感到非常兴奋。 我们已经写了一些关于Java 8好东西的博客 &#xff0c;现在我们觉得是时候开始一个…

音频光端机与电话光端机区别

在网络安防工程中&#xff0c;我们应用的比较多有电话光端机和音频光端机&#xff0c;但是在工程应用中&#xff0c;我们所用到的音频光端机是不同于电话光端机的应用。那么&#xff0c;音频光端机与电话光端机具体有哪些区别呢&#xff1f;接下来就跟随飞畅科技的小编一起来详…

[渝粤教育] 南开大学 思辨式英文写作 参考 资料

教育 -思辨式英文写作-章节资料考试资料-南开大学【】 随堂小测&#xff1a;What are the characteristics of critical essays? 1、【多选题】Which of the following types of writing are critical essays? A、A movie review B、A news report C、An editorial D、A summ…

辨别光纤收发器优劣的方法介绍

提起光纤收发器&#xff0c;不得不说它的传输稳定与传输距离。但质量不好的光纤收发器&#xff0c;要受到各种各样的考验&#xff0c;那么&#xff0c;我们该怎么区别出质量不好的光纤收发器呢&#xff1f;今天&#xff0c;飞畅科技的小编就来为大家详细介绍下辨别光纤收发器优…

一文解读光纤收发器单模和多模的区别!

光纤收发器是进行光电信号转换的设备&#xff0c;现在光纤收发器的技术越发成熟&#xff0c;应用也越来越广泛&#xff0c;所以我们在选择或者采购光纤收发器时&#xff0c;对光纤收发器做一定的了解是有好处的&#xff0c;接下来我们就来给大家详细介绍一下光纤收发器的单模和…

打造可复制爆款短视频的底层逻辑

大家都在短视频平台上做生意&#xff0c;给短视频平台带来了什么&#xff1f; 谁能满足平台需求&#xff0c;就给谁流量 谁能达成平台指标&#xff0c;就给谁流量 如何达成平台各项指标&#xff0c;做大V? 一、流量算法 曝光流量 Ya.X1 b.X2 c.X3 d.X4 e.X5 ... z.X23…

工业级光纤收发器与光端机各自的作用及区别介绍

工业通信行业的朋友应该都知道&#xff0c;光端机和光纤收发器都是一种可以进行光电转换的通信设备&#xff0c;它们之间有很多类似的功能。既然这样&#xff0c;那它们能否通用呢&#xff1f;接下来&#xff0c;就由飞畅科技的小编来给大家解释说明下光纤收发器和光端机各自的…

[渝粤教育] 厦门理工学院 机械设计 参考 资料

教育 -机械设计-章节资料考试资料-厦门理工学院【】 第三单元测验 1、【单选题】在常用的螺旋传动中&#xff0c;传动效率最高的螺纹是 A、三角形螺纹 B、梯形螺纹 C、锯齿形螺纹 D、矩形螺纹 参考资料【 】 2、【单选题】螺纹连接防松的根本目的是 A、增加螺纹连接的轴向力 B、…

光纤收发器A,B端含义解释

最近有朋友问到&#xff0c;光纤收发器型号或者收发器模块上A,B字母的含义是什么&#xff1f;今天飞畅科技的小编就来为大家介绍一下&#xff0c;收发器中A,B端字母的真正含义。一起来看看吧&#xff01; 首先,光纤收发器按光纤芯数分类有2种&#xff0c;一种是单模双纤光纤收…

光纤收发器按照网管怎么分类

光纤收发器&#xff0c;是一种将短距离的双绞线电信号和长距离的光信号进行互换的以太网传输媒体转换单元&#xff0c;在很多地方也被称之为光电转换器&#xff08;Fiber Converter&#xff09;。光纤收发器按照网管分类可分为网管型以太网光纤收发器和非网管型以太网光纤收发器…

html嵌入war_WAR文件与具有嵌入式服务器的Java应用程序

html嵌入war大多数服务器端Java应用程序&#xff08;例如&#xff0c;面向Web或面向服务的&#xff09;都打算在容器中运行。 打包这些应用程序以进行分发的传统方法是将它们捆绑为WAR文件。 这只不过是具有标准目录布局的ZIP存档&#xff0c;其中包含运行时所需的所有库和应用…