如何使用Apache Shiro和OAuth 2.0构建安全的应用程序


对于那些不熟悉的人,ApacheShiro(一种Java安全框架)执行授权,身份验证和会话管理(以及许多其他功能),以帮助构建更安全的应用程序。

这篇文章将向您展示如何使用JAX-RS来构建一个简单的JavaREST应用程序。JAX-RS是一组接口,因此您需要选择实现。在本文中,我们将使用Jersey—但是您可以使用自己喜欢的任何实现,而且这些API都不是Jersey特有的。

在OAuth 2.0中,REST服务通常是资源服务器。简单来说,它们使用在Authorization HTTP标头中发送的访问令牌进行身份验证,格式为Authorization:Bearer <access-token>

创建一个新的JAX-RS项目 有几种创建新的基于Maven的项目的方法。我通常使用我的IDE,但是您也可以在命令行上生成一个。无论采用哪种方式,都应从一个如下所示的pom.xml文件开始:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.okta.example</groupId>
    <artifactId>okta-shiro-jaxrs-example</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

</project>

接下来,添加依赖项:

<dependencies>
        <dependency>
            <groupId>com.okta.shiro</groupId>
            <artifactId>okta-shiro-plugin</artifactId>
            <version>0.1.0</version>
        </dependency>
        <dependency>
            <groupId>javax.ws.rs</groupId>
            <artifactId>javax.ws.rs-api</artifactId>
            <version>2.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-jaxrs</artifactId>
            <version>1.5.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-servlet-plugin</artifactId>
            <version>1.5.3</version>
            <scope>runtime</scope>
        </dependency>
        <!-- logging -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>1.7.30</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
           <version>1.2.3</version>
            <scope>runtime</scope>
        </dependency>
        <!-- JAX-RS, runtime only dependencies -->
        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet</artifactId>
            <version>2.30.1</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-server</artifactId>
            <version>2.30.1</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jersey.inject</groupId>
            <artifactId>jersey-hk2</artifactId>
            <version>2.30.1</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

为了使运行WAR文件变得容易,我们可以将Jetty Maven插件添加到pom文件中:

<build>
        <plugins>
            <plugin>
                <groupId>org.eclipse.jetty</groupId>
               <artifactId>jetty-maven-plugin</artifactId>
                <version>9.4.27.v20200227</version>
                <configuration>
                    <httpConnector>
                        <port>8000</port>
                    </httpConnector>
                </configuration>
            </plugin>
        </plugins>
    </build>

创建一个JAX-RS端点 JAX-RS应用程序至少包含两部分:用于处理请求的REST资源/端点,以及将Application它们全部保持在一起的类。这些资源只是具有注释的Java对象,这些注释将HTTP请求映射到方法。

创建一个简单的资源,以在其中显示当前用户的电子邮件地址 src/main/java/com/okta/example/shiro/SecureEndpoint.java

package com.okta.example.shiro;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.SecurityContext;
@Path("/") 
@Produces({"plain/text"}) 
public class SecureResource {
    @GET 
    @RequiresAuthentication 
    public String showUser(@Context SecurityContext securityContext) { 
        return "Current User: " + securityContext.getUserPrincipal().getName(); 
    }
}
  1. 此类中所有方法的基本路径
  2. 在这篇文章中保持简单,只返回纯文本
  3. 此方法将处理HTTPGET请求要求身份验证!
  4. 注入当前用户的安全上下文
  5. 从Java主体获取名称

如果您需要从访问令牌中获取其他信息,请将用户主体转换为OktaJwtPrincipal并使用以下getClaim()方法:

OktaJwtPrincipal jwtPrincipal = (OktaJwtPrincipal) securityContext;
jwtPrincipal.getClaim("your-claim-key");

创建一个JAX-RS应用程序 JAX-RSApplication类定义与应用程序关联的元数据和组件。大多数JAX-RS实现都提供帮助程序类,该类自动扫描您的资源,但是由于此示例可与任何实现一起使用,因此您将直接配置它们。

创建一个从延伸的类Application中src/main/java/com/okta/example/shiro/RestApplication.java:

package com.okta.example.shiro;
import org.apache.shiro.web.jaxrs.ShiroFeature;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;
@ApplicationPath("/") 
public class RestApplication extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> classes = new HashSet<>();
        classes.add(ShiroFeature.class); 
        classes.add(SecureResource.class); 
        return classes;
    }
}
  1. 此应用程序已安装到/,所有资源路径都与此相对
  2. 注册Apache Shiro的JAX-RS功能
  3. 添加我们在上一步中创建的SecureResource

配置Apache Shiro以使用OAuth 2.0 可以通过几种不同的方式配置Apache Shiro:以编程方式,通过Spring和Guice进行依赖注入,或使用“ ini”文件。为了使事情更加集中,我将使用shiro.ini位于中的一个简单文件src/main/resources:

[main]
# Define the Okta realm
oktaJwtRealm = com.okta.shiro.realm.OktaResourceServerRealm
# Configure your issuer
oktaJwtRealm.issuer = https://{yourOktaDomain}/oauth2/default
[urls]
# use the `authcBearer` filter to process Bearer tokens
/** = authcBearer

如果您有需要匿名访问的资源,请使用-authcBearer[permissive]只需确保正确注释了所有端点!

添加一个 web.xml 您可能会问自己“真的是web.xml文件吗?” 从技术上讲,您不需要一个,而可以将Maven War Plugin配置为不需要web.xml。

或者,只需将一个空添加web.xml到src/main/webapp:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee https://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
</web-app>

运行安全的REST应用程序 您可以使用构建项目./mvnw package。只需从target目录中获取war文件,将其复制到您喜欢的容器中,然后启动它。相反,我们将使用Jetty Maven插件。在项目目录中,运行:

curl localhost:8000/ -v

此命令启动在port上运行的服务器8000。使用curl发出请求:

< HTTP/1.1 401 Unauthorized
< Date: Thu, 09 Apr 2020 17:50:49 GMT
< WWW-Authenticate: Bearer realm="application"
< Content-Length: 0
< Server: Jetty(9.4.27.v20200227)

服务器401未提供访问令牌,因此返回了状态代码。有几种获取访问令牌的方法。哪种选择最适合您,取决于您在何处以及如何访问REST应用程序。通常,正在调用REST API的应用程序已经具有访问令牌。例如,SPA移动应用程序或另一个Web应用程序可能已经具有经过身份验证的用户。为了进行测试,我们将设置OIDC调试器。

创建一个OAuth 2.0应用程序 登录到您的Okta管理控制台。如果您刚创建一个新的Okta帐户但尚未登录,请单击收件箱中的激活链接。

在右上角记下组织URL;我将{yourOktaDomain}在下一节中提到它。

登录后,从顶部菜单中选择应用程序→添加应用程序。然后,选择Web →下一步。

为您的应用程序命名,例如:“ Shiro JAX-RS Example”。

将登录重定向URI设置为https://oidcdebugger.com/debug

检查隐式(Hybrid)

点击完成

1596044614919.png

应用程序设置记下Client ID,下一步将需要它。

使用OIDC调试器获取令牌 转到https://oidcdebugger.com/,并使用以下值填充表单:

  • Authorize URI - -{yourOktaDomain}/oauth2/default/v1/authorize

  • ClientID -{yourClientID}从之前的步骤

  • State- this is a test(可以是任何值)

  • Response type-选择令牌

  • 对所有其他字段使用默认值

按下发送请求按钮。

如果您使用隐身浏览器/专用浏览器,这可能会提示您再次登录。一旦成功页面加载,请复制访问令牌,并创建一个环境变量:

export TOKEN=" <your-access-token-here>"

现在有了令牌,您可以向您的JAX-RS服务器发出另一个请求:

curl localhost:8000/ -H "Authorization: Bearer $TOKEN"
Current User: <your-email-address>

就像这样,您已经向您的JAX-RS应用程序发出了经过身份验证的请求!

了解有关安全应用程序的更多信息 在本教程中,我向您展示了如何使用Apache Shiro和Okta保护简单的JAX-RS应用程序。这种相同的资源服务器技术也可以与其他基于servlet的Web应用程序一起使用。


原文链接:http://codingdict.com/