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 工厂模式自动注入到Map

    使用SpringBoot工厂模式自动注入到Map的完整攻略 在Spring Boot中,我们可以使用工厂模式将一组相关的类进行组合,并将它们自动注入到Map中。这种方式可以帮助我们更加灵活地管理和使用这些类。本文将详细讲解使用Spring Boot工厂模式自动注入到Map的完整攻略,包括以下内容: 工厂模式的基本概念 Spring Boot中的工厂模式实现方…

    Java 2023年5月15日
    00
  • Java实现PDF转为Word文档的示例代码

    为了实现Java将PDF转为Word文档,我们可以借助开源库Apache POI和iText库来实现。具体步骤如下: 步骤一: 导入jar包 首先需要下载和导入两种jar包:Apache POI和iText。可以通过Maven或手动下载jar包。 Maven依赖: <dependency> <groupId>org.apache.po…

    Java 2023年5月19日
    00
  • Java 实战项目锤炼之嘟嘟健身房管理系统的实现流程

    Java 实战项目锤炼之嘟嘟健身房管理系统的实现流程 一、需求分析 系统包含三个角色:管理员、健身房教练、健身房会员。 系统功能: 管理员:添加教练、添加会员、管理教练、管理会员、查看销售业绩。 教练:发布健身计划、管理个人信息。 会员:查看健身计划、预约课程、管理个人信息。 系统特色功能:会员课程提醒、打卡记录、数据分析。 二、技术选型 开发语言:Java…

    Java 2023年5月24日
    00
  • 使用maven-archetype-plugin现有项目生成脚手架的方法

    使用maven-archetype-plugin插件可以快速生成符合规范的项目结构。下面是使用该插件生成脚手架的方法: 确保已经安装了Maven,并配置好了环境变量; 进入项目工程的根目录,执行以下命令: mvn archetype:create-from-project 然后等待项目构建成功。构建完成后,会在生成一个target目录。该目录下会生成一个ma…

    Java 2023年5月20日
    00
  • mybatis and,or复合查询操作

    下面是关于 Mybatis 中 AND 和 OR 复合查询操作的完整攻略。 基础知识 在 Mybatis 中,我们可以使用 <where> 元素来构造复杂的条件语句,其中包含了 AND 和 OR 连接符。如下所示: <select id="selectByExample" resultMap="BaseResu…

    Java 2023年5月20日
    00
  • PTA题目集4~6总结

    一前言   题目集四主要考察的是对LocalDate,ArrayList,HashSet等Java自带类的使用   题目集五主要考察的是对正则表达式的使用,以及对其题目集三的时间题目的进行类结构的改变   题目集六只有一道题,主要是对题目集四的第一题进行加大难度   总的来说这几次的题目量比前面几次都要少,但是题目难度开始加深。 二设计与分析    由于部分…

    Java 2023年4月30日
    00
  • 一篇文章弄懂Java8中的时间处理

    一篇文章弄懂Java8中的时间处理 在Java8中,新增加了新的时间API,旨在解决以前日期和时间类的API存在的一些问题。本文将介绍如何使用Java8中的时间处理,包括时间表示、时间的计算和转换。 时间表示 Java8引入了新的时间类,代表了不同类型的日期时间。下面列出了其中一些常用的时间类: LocalDate:只包含日期,即年月日 LocalTime:…

    Java 2023年5月20日
    00
  • java连接orcale数据库示例分享

    下面是详细的攻略。 Java连接Oracle数据库 准备工作 在开始之前,我们需要确保已经完成以下准备工作: 安装Oracle数据库:在官网中下载并安装Oracle数据库,安装完成后配置好环境变量。 下载Oracle JDBC驱动:在官网中下载对应版本的JDBC驱动,将其复制到Java项目中。 示例1:使用JDBC API进行数据库操作 以下是连接Oracl…

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