Spring Security整合KeyCloak保护Rest API实现详解

Spring Security整合KeyCloak保护Rest API实现详解

简介

本篇文章主要介绍如何使用Spring Security整合KeyCloak保护Rest API。

前置条件

在开始本文之前,你应该已经了解过以下知识:

  1. Spring Boot
  2. Spring Security
  3. Rest API设计基础
  4. OAuth2.0和OpenID Connect协议的基本知识

同时,在开始之前,你应该已经完成以下准备工作:

  1. 下载并安装KeyCloak
  2. 创建一个Spring Boot项目

实现步骤

步骤一:引入KeyCloak依赖

pom.xml 文件中添加以下依赖:

<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-spring-boot-starter</artifactId>
    <version>15.0.2</version>
</dependency>

步骤二:配置KeyCloak客户端

application.yml 文件中添加以下配置:

keycloak:
  auth-server-url: <KeyCloak服务器地址>
  realm: <Realm名称>
  resource: <客户端名称>
  credentials:
    secret: <客户端密码>

步骤三:配置Spring Security

WebSecurityConfig 类中添加以下代码:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    /**
     * 密码加密方式
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    /**
     * 配置安全过滤器链
     */
    @Bean
    public KeycloakConfigResolver KeycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.authorizeRequests()
                .antMatchers(HttpMethod.GET, "/api/public/**").permitAll()
                .antMatchers(HttpMethod.POST, "/api/public/**").permitAll()
                .antMatchers(HttpMethod.PUT, "/api/public/**").permitAll()
                .anyRequest().authenticated();
    }

}

代码解释:

  1. 继承 KeycloakWebSecurityConfigurerAdapter 后,Spring Security 就可以通过KeyCloak进行身份验证了。
  2. configureGlobal 方法用来设置身份验证的方式。在这里,我们使用KeyCloak身份验证。
  3. configure 方法用来配置安全过滤器链。这里我们配置了一个公开的API和一个安全的API。公开的API不需要进行身份验证。

步骤四:测试

我们可以通过以下代码来测试我们的API:

@RestController
@RequestMapping("/api")
public class TestController {

    @GetMapping("/public/hello")
    public String publicHello() {
        return "Hello, Public!";
    }

    @GetMapping("/hello")
    public String hello() {
        return "Hello, Protected!";
    }

}

其中,/api/public/hello 是公开的API,不需要进行身份验证。/api/hello 是需要进行身份验证的API。

我们可以使用 httpie 工具来测试:

$ http GET localhost:8080/api/public/hello
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Date: Thu, 28 Oct 2021 07:57:20 GMT
Transfer-Encoding: chunked

Hello, Public!

$ http GET localhost:8080/api/hello
HTTP/1.1 401
Set-Cookie: KC_RESTART=eyJhbGciOi...[省略]...Sjh-RA; Path=/auth/realms/Test/account/;
WWW-Authenticate: Bearer realm="Test", error="unauthorized", error_description="Bearer token is not valid"
Content-Length: 0

可以看到,公开的API返回了 Hello, Public! 而安全的API返回了401错误。

步骤五:使用Postman进行测试

我们可以使用Postman工具来测试我们的API。首先,我们需要获取到一个AccessToken。我们可以使用以下代码来获取AccessToken:

@Service
public class KeyCloakServiceImpl implements KeyCloakService {

    @Autowired
    private KeycloakSpringBootProperties properties;

    @Autowired
    private KeycloakClientRequestFactory factory;

    @Override
    public AccessTokenResponse getAccessToken(String username, String password) throws IOException, KeycloakServiceException {
        String accessTokenUrl = properties
                .getAuthServerUrl()
                .concat("/realms/")
                .concat(properties.getRealm())
                .concat("/protocol/openid-connect/token");
        HttpClient httpClient = factory.createHttpClient();
        HttpPost httpPost = new HttpPost(accessTokenUrl);

        List<NameValuePair> formParams = Arrays.asList(
                new BasicNameValuePair("client_id", properties.getResource()),
                new BasicNameValuePair("username", username),
                new BasicNameValuePair("password", password),
                new BasicNameValuePair("grant_type", "password")
        );

        httpPost.setEntity(new UrlEncodedFormEntity(formParams));

        HttpResponse response = httpClient.execute(httpPost);

        if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
            throw new KeycloakServiceException("Failed to get access token");
        }

        ObjectMapper mapper = new ObjectMapper();
        String result = EntityUtils.toString(response.getEntity());
        return mapper.readValue(result, AccessTokenResponse.class);
    }

}

其中, AccessTokenResponse 是一个包含AccessToken等信息的DTO类。

现在我们已经获得了AccessToken了,我们可以使用Postman工具来测试我们的API。我们首先需要在Postman中设置相应的Header:

  • Authorization:Bearer AccessToken

然后我们就可以使用Postman工具来测试我们的API了。可以看到,公开的API返回了 Hello, Public! 而安全的API返回了401错误。

示例1:如何在Spring Security中使用自定义身份验证方式

有时候我们需要使用自定义的身份验证方式来替代KeyCloak进行身份验证。这里我们举一个例子,介绍如何实现该功能。

假设我们有一个API需要使用自定义身份验证方式。这个API是 /api/custom/hello,具有如下代码:

@RestController
@RequestMapping("/api/custom")
public class CustomController {

    @GetMapping("/hello")
    public String hello() {
        return "Hello, Custom!";
    }

}

我们需要使用带有 Authorization 头的请求进行测试:

$ http GET localhost:8080/api/custom/hello
HTTP/1.1 401
WWW-Authenticate: Custom
Content-Length: 0

可以看到该API返回了401错误,表示该API需要进行身份验证。

我们可以使用以下代码来实现自定义身份验证:

@Configuration
public class CustomSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers(HttpMethod.GET, "/api/custom/**").authenticated()
                .and()
                .addFilterBefore(new CustomAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    public static class CustomAuthenticationFilter extends OncePerRequestFilter {

        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            String authorizationHeader = request.getHeader("Authorization");
            if (authorizationHeader != null && authorizationHeader.startsWith("Custom ")) {
                filterChain.doFilter(request, response);
                return;
            }
            response.setHeader("WWW-Authenticate", "Custom");
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
        }

    }

}

代码解释:

  1. 我们通过 configure 方法来配置安全过滤器链。进行自定义身份验证的API需要进行身份验证。
  2. 我们通过 addFilterBefore 方法来添加我们自定义的身份验证过滤器。该过滤器会在Spring Security的默认身份验证过滤器之前执行。
  3. CustomAuthenticationFilter 类是自定义的身份验证过滤器。它会检查请求头中是否包含 Authorization: Custom 。如果包含,则表示身份验证成功;否则返回401错误,并在响应头中包含 WWW-Authenticate: Custom 字段。

现在我们重新发起请求:

$ http GET localhost:8080/api/custom/hello
HTTP/1.1 401
WWW-Authenticate: Custom
Content-Length: 0

可以看到API返回了401错误,但是响应头中包含了 WWW-Authenticate: Custom 字段。这意味着我们已经成功地进行了自定义身份验证。

示例2:如何获取用户信息

在实际开发中,我们通常需要获取当前登录用户的信息。我们可以使用以下代码:

@RestController
@RequestMapping("/api")
public class UserController {

    @GetMapping("/user")
    public ResponseEntity<?> user(Authentication authentication) {
        if (authentication == null) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("User not found");
        }
        return ResponseEntity.ok(authentication.getPrincipal());
    }

}

其中,我们可以通过注入 Authentication 对象来获取当前登录用户的信息。如果没有登录,则返回401错误。

总结

本文介绍了如何使用Spring Security整合KeyCloak保护Rest API。同时,我们还介绍了如何使用自定义身份验证方式和如何获取用户信息。希望能对大家有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring Security整合KeyCloak保护Rest API实现详解 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • Java图形化界面设计之容器(JFrame)详解

    Java图形化界面设计之容器(JFrame)详解 1. 容器的概念 在Java图形化界面设计中,容器指的是能够包含其他可视组件(如按钮、文本框等)的组件。容器可以是顶层容器(如JFrame、JDialog等)或内部容器(如JPanel、JTabbedPane等)。 JFrame是一个非常常用的顶层容器,它是Java AWT中的Frame类的一个子类,在Swi…

    Java 2023年5月23日
    00
  • JSP使用JDBC完成动态验证及采用MVC完成数据查询的方法

    JSP使用JDBC完成动态验证及采用MVC完成数据查询的方法 本文将详细讲解如何通过JSP使用JDBC完成动态验证及采用MVC完成数据查询的方法。步骤分为以下几个部分: I. JDBC动态验证 动态验证可以确保用户提供的输入数据是正确的。如果用户提供的数据无法通过验证,应该向用户显示错误消息。JDBC是Java语言访问关系型数据库的标准API。 以下是通过J…

    Java 2023年5月20日
    00
  • java如何获取本地操作系统进程列表

    获取本地操作系统进程列表可以使用Java自带的管理类java.lang.management.ManagementFactory和java.lang.management.RuntimeMXBean。 首先,我们需要通过ManagementFactory类的getRuntimeMXBean()方法获得当前运行时的RuntimeMXBean对象,然后即可调用该…

    Java 2023年5月24日
    00
  • JSP/Servlet应用程序优化八法

    JSP/Servlet应用程序优化八法,是指在开发和维护JSP/Servlet应用程序时,为提高应用程序性能和可维护性而采取的八项优化策略。以下是这八项优化策略的详细讲解。 一、使用JSTL标签库 JSTL是Java服务器页面标准标签库,它是JSP页面处理的标准解决方案。使用JSTL标签库可以有效地减少JSP页面中的Java代码,提高页面的可读性和可维护性。…

    Java 2023年6月15日
    00
  • MyBatis 详细讲解动态 SQL的使用

    MyBatis 详细讲解动态 SQL的使用 MyBatis是一个支持动态SQL的持久层框架,可以使用简单的XML或注解进行配置。动态SQL是指能够在运行时根据不同条件生成不同SQL语句的能力。这种能力使我们能够构建出非常灵活的SQL语句,从而更好地满足项目需求。在本文中,我们将学习如何使用MyBatis的动态SQL。 1. if 标签 if 标签用来在满足一…

    Java 2023年5月20日
    00
  • java 文件流的处理方式 文件打包成zip

    Java文件流的处理方式是 Java IO 提供的一种输入输出流 API。Java 的 IO 包提供了对外部数据来源和写入运行环境的能力,可以用于本地文件、网络资源、内存缓冲区等。Java IO 分为输入流和输出流两部分,其中输入流主要负责读取数据,而输出流则负责写入数据到指定位置。 Java 中可以使用java.util.zip和java.io包中提供的压…

    Java 2023年5月19日
    00
  • springboot整合 beatlsql的实例代码

    下面我将为您详细讲解如何将Spring Boot与BeetlSQL整合。 一、Spring Boot集成BeetlSQL的前置条件 在开始整合前,请确保您拥有以下环境和工具: JDK1.8及以上版本 Maven3.0及以上版本 Spring Boot 2.0.0及以上版本 BeetlSQL 2.x版本(本示例使用的是2.8.2版本) 二、创建Spring B…

    Java 2023年5月20日
    00
  • JAVA 深层拷贝 DeepCopy的使用详解

    JAVA 深层拷贝 DeepCopy的使用详解 什么是深度拷贝? 在JAVA中,如果需要拷贝一个对象,可以使用浅拷贝shallow copy方法。这种方法只是复制了一个引用,当对原始对象进行修改时,复制对象也会发生相应的修改。这是因为原始对象和复制对象只是引用同一地址。而深度拷贝就是完全的副本,不仅对象本身被复制,对象内部的变量和引用同样被复制。 深层拷贝的…

    Java 2023年5月26日
    00
合作推广
合作推广
分享本页
返回顶部