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日

相关文章

  • Angular.Js中ng-include指令的使用与实现

    Angular.js中ng-include指令的使用与实现 ng-include是Angular.js提供的一个指令,用于在页面中引入外部HTML文件。 使用方法 我们可以在需要引用的地方,使用ng-include指令,如下所示: <div ng-include="’path/to/your/template.html’">&…

    Java 2023年6月15日
    00
  • Java中的ArrayIndexOutOfBoundsException是什么?

    ArrayIndexOutOfBoundsException是Java中的一个异常类,用于处理数组下标越界的情况。当数组的下标越界时,抛出该异常。 以下是一个简单的示例: int[] arr = new int[5]; arr[6] = 10; 上述示例中,数组arr的长度为5,但我们试图使用下标6来访问该数组。由于数组的大小为5,因此下标必须在0到4之间。…

    Java 2023年4月27日
    00
  • Java中Arrays类与Math类详解

    Java中Arrays类与Math类详解 在Java中,Arrays类和Math类是常用的工具类,主要提供了一些静态方法来方便我们进行数组、数值计算等操作。 Arrays类 Arrays类提供了很多有用的方法来进行数组的操作,包括数组的排序、查找、复制等。 数组排序 排序算法 Arrays类中提供了sort()方法来对数组进行排序,在方法中我们可以通过传入C…

    Java 2023年5月26日
    00
  • java的Hibernate框架报错“UnknownEntityTypeException”的原因和解决方法

    当使用Java的Hibernate框架时,可能会遇到“UnknownEntityTypeException”错误。这个错误通常是由于以下原因之一引起的: 实体类未被正确映射:如果您的实体类未被正确映射,则可能会出现此错误。在这种情况下,需要检查您的实体类映射以解决此问题。 实体类名称错误:如果您的实体类名称错误,则可能会出现此错误。在这种情况下,需要检查您的…

    Java 2023年5月4日
    00
  • Java刷视频浏览量点赞量的实现代码

    为了实现Java刷视频浏览量和点赞量,需要进行如下步骤: 1. 获取视频链接 首先需要获取视频的地址。这可以通过抓包工具来获取,或者通过视频网站提供的API来获取。比如,在B站网站中,可以使用下面的API获取某一个视频的详细信息: https://api.bilibili.com/x/web-interface/view?aid={aid} 其中{aid}是…

    Java 2023年6月16日
    00
  • Log4j不同模块输出到不同的文件中

    要实现Log4j不同模块输出到不同的文件中,需要使用配置文件。下面是实现此功能的步骤: 创建Log4j配置文件 在项目中,创建一个名为log4j.properties或log4j.xml的配置文件,并将其放在类路径下(src/main/resources目录下)。这个配置文件需要定义多个输出端,每个输出端和对应的日志级别,以及如何输出。一个简单的log4j配…

    Java 2023年5月19日
    00
  • 吊打Java面试官之Lambda表达式 Stream API

    吊打Java面试官之Lambda表达式 Stream API 在Java 8中,Lambda表达式和Stream API是两个非常强大和重要的功能,能够帮助开发人员编写更简洁、更灵活的代码。下面我们将详细讲解Lambda表达式和Stream API的使用方法。 Lambda表达式 Lambda表达式是一种匿名函数,可以将其视为一种简洁而强大的快捷方式,可以在…

    Java 2023年5月26日
    00
  • Javassist用法详解

    Javassist用法详解 Javassist是一个Java字节码操作库,它可以在运行时修改字节码从而对Java类进行动态编辑和代码生成。Javassist可以用于许多Java开发工具,例如实现AOP(面向切面编程)框架,实现ORM(对象关系映射)框架,实现动态代理等。 基本用法 在使用Javassist之前,我们需要在项目中引入Javassist的依赖: …

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