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日

相关文章

  • springboot之Jpa通用接口及公共方法使用示例

    下面是对“springboot之Jpa通用接口及公共方法使用示例”的完整攻略。 一、背景 Spring Boot 是基于Spring的快速开发的一个微框架,而JPA(Java Persistence API)是一种Java ORM框架。 二、Jpa通用方法 JPA提供了一系列的通用接口和公共方法,我们可以直接调用,不用手写SQL语句。以下列出几个常用的通用方…

    Java 2023年5月20日
    00
  • Java使用jni清屏功能的实现(只针对cmd)

    下面是关于Java使用JNI清屏功能的实现攻略。 1. 概述 Java中使用JNI可以调用C代码,因此我们可以使用C代码实现一些Java无法直接实现的功能。本文将介绍如何使用JNI实现Java清屏功能(只针对cmd)。 2. 具体实现 2.1 JNI代码 我们需要编写C代码来实现清屏操作。以下是一个简单的C代码示例,可以实现Windows下的清屏操作: #i…

    Java 2023年5月26日
    00
  • java之Object类用法实例

    Java之Object类用法实例 在Java中,所有的类都是继承自Object类,因此Object类是Java中最基本的类之一。本文将详细讲解Object类的用法,包括几个重要的方法以及示例说明。 Java Object类的方法 toString() toString() 方法是Object类中最基本的方法之一,通常用于返回对象的字符串表示。默认情况下,to…

    Java 2023年5月26日
    00
  • MyBatis批量添加数据2种实现方法

    以下是MyBatis批量添加数据2种实现方法的完整攻略。 1. 方式一:foreach元素进行插入 在MyBatis中,我们可以使用foreach元素实现批量插入。具体步骤如下: 在MyBatis的Mapper配置文件中,编写插入语句,并使用foreach元素将多个数据插入到表中。示例代码如下: <insert id=”batchInsert”>…

    Java 2023年5月20日
    00
  • 详解Java利用深度优先遍历解决迷宫问题

    详解Java利用深度优先遍历解决迷宫问题 简介 在计算机科学中,深度优先遍历是一种用于遍历或搜索树或图的概念。深度优先遍历会先访问深度最大的节点(或者最右边的节点),然后回溯到该节点的父节点,并开始遍历它的另一个子节点。这个过程会一直持续到所有的节点都被访问为止。 用深度优先遍历算法解决迷宫问题可以思路简单易懂,代码编写也相对比较简单。 实现步骤 1. 定义…

    Java 2023年5月19日
    00
  • Java使用System.currentTimeMillis()方法计算程序运行时间的示例代码

    下面我来详细讲解使用Java中的System.currentTimeMillis()方法计算程序运行时间的完整攻略。 一、System.currentTimeMillis()方法 System.currentTimeMillis()是Java中的一个常用方法,用于获取当前时间戳,即从1970年1月1日0时0分0秒开始到现在的毫秒数,返回类型为long。 二、…

    Java 2023年5月20日
    00
  • Springmvc加ajax实现上传文件并页面局部刷新

    首先,上传文件是指将文件从客户端传输到服务器端,而Springmvc是一种轻量级的mvc框架。在本文中,将会介绍如何利用Springmvc和ajax实现文件上传和页面局部刷新。 一、环境准备 实现文件上传需要用到Springmvc和Spring的MultipartResolver组件,因此需要在pom.xml文件中引入相关依赖。 <!– Spring…

    Java 2023年6月15日
    00
  • springboot返回值转成JSONString的处理方式

    下面是 “springboot返回值转成JSONString的处理方式” 的完整攻略。 什么是返回值转成JSONString的处理方式 当我们在使用springboot开发web应用时,我们需要将后端代码返回的对象转换成前端可识别的JSON格式。这时我们就需要采用某种处理方式。 如何将返回值转换成JSONString 方法一:使用Jackson Jackso…

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