spring boot使用前后端分离的应用使用Keycloak做集成认证(spring security)

spring boot使用前后端分离的应用使用Keycloak做集成认证(spring security)

2838发表于2020-07-05

前面我写了一篇:postman Keycloak保护Spring Boot Restful API接口Spring Security整合实例

接下来介绍一下spring boot未前后端分离的应用使用Keycloak做集成认证。

当然这里spring boot也是用的spring security。

一、配置keycloak

1、创建client

下面创建一个client是spring boot应用需要用到的client。

from clipboard

注意:最下面的Redirect URIs是回调地址这个是填写Spring Boot应用地址,最后的星号表示所有路径。

2、创建用户

添加一个用户用来验证跳转登录

from clipboard

保存之后,设置用户密码。

from clipboard

最后,为demo用户添加访问角色(Member与Libraian),之前我们创建好的。这个角色名要与下面Spring Boot应用中的对应。

from clipboard

二、Spring Boot项目

最终的项目结构如下

from clipboard

下面介绍最核心的代码。

application.properties:

server.port=9090
keycloak.realm=master
keycloak.resource=app1
keycloak.auth-server-url=http://10.241.89.58:8080/auth
keycloak.ssl-required=external
keycloak.public-client=true
keycloak.principal-attribute=preferred_username
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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.lanhusoft</groupId>
    <artifactId>Keycloak-Demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <oauth2-autoconfigure.version>2.1.6.RELEASE</oauth2-autoconfigure.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>${oauth2-autoconfigure.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-oauth2-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-oauth2-jose</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.keycloak/keycloak-spring-boot-starter -->
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-spring-boot-starter</artifactId>
            <version>7.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

</project>

添加类KeycloakConfig,SpringBoot与keycloak集成:

package com.lanhusoft.config;

import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Copyright www.lanhusoft.com
 * Author:Apex Zheng
 * Date:2019/12/11
 * Description:
 */
@Configuration
public class KeycloakConfig {

    @Bean
    public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }
}
添加类SecurityConfig,SpringSecurity与keycloak集成:
package com.lanhusoft.config;

import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.keycloak.adapters.springsecurity.management.HttpSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;

/**
 * Copyright www.lanhusoft.com
 * Author:Apex Zheng
 * Date:2019/12/11
 * Description:
 */
@KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
    /**
     * Registers the KeycloakAuthenticationProvider with the authentication manager.
     *
     * Since Spring Security requires that role names start with "ROLE_",
     * a SimpleAuthorityMapper is used to instruct the KeycloakAuthenticationProvider
     * to insert the "ROLE_" prefix.
     *
     * e.g. Librarian -> ROLE_Librarian
     *
     * Should you prefer to have the role all in uppercase, you can instruct
     * the SimpleAuthorityMapper to convert it by calling:
     * {@code grantedAuthorityMapper.setConvertToUpperCase(true); }.
     * The result will be: Librarian -> ROLE_LIBRARIAN.
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        SimpleAuthorityMapper grantedAuthorityMapper = new SimpleAuthorityMapper();
        grantedAuthorityMapper.setPrefix("ROLE_");

        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(grantedAuthorityMapper);
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    /**
     * Defines the session authentication strategy.
     *
     * RegisterSessionAuthenticationStrategy is used because this is a public application
     * from the Keycloak point of view.
     */
    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    /**
     * Define an HttpSessionManager bean only if missing.
     *
     * This is necessary because since Spring Boot 2.1.0, spring.main.allow-bean-definition-overriding
     * is disabled by default.
     */
    @Bean
    @Override
    @ConditionalOnMissingBean(HttpSessionManager.class)
    protected HttpSessionManager httpSessionManager() {
        return new HttpSessionManager();
    }

    /**
     * Define security constraints for the application resources.
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http
                .authorizeRequests()
                .antMatchers("/books").hasAnyRole("Member", "Librarian")
                .antMatchers("/manager").hasRole("Librarian")
                .anyRequest().permitAll();
    }
}

添加一个需要认证保护的访问资源LibraryController:

package com.lanhusoft.controller;

import com.lanhusoft.Repository.BookRepository;
import org.keycloak.KeycloakSecurityContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

/**
 * Copyright www.lanhusoft.com
 * Author:Apex Zheng
 * Date:2019/12/11
 * Description:
 */
@Controller
public class LibraryController {
    private final HttpServletRequest request;
    private final BookRepository bookRepository;

    @Autowired
    public LibraryController(HttpServletRequest request, BookRepository bookRepository) {
        this.request = request;
        this.bookRepository = bookRepository;
    }

    @GetMapping(value = "/")
    public String getHome() {
        return "index";
    }

    @GetMapping(value = "/books")
    public String getBooks(Model model) {
        configCommonAttributes(model);
        model.addAttribute("books", bookRepository.readAll());
        return "books";
    }

    @GetMapping(value = "/manager")
    public String getManager(Model model) {
        configCommonAttributes(model);
        model.addAttribute("books", bookRepository.readAll());
        return "manager";
    }

    @GetMapping(value = "/logout")
    public String logout() throws ServletException {
        request.logout();
        return "redirect:/";
    }

    private void configCommonAttributes(Model model) {
        model.addAttribute("name", getKeycloakSecurityContext().getIdToken().getGivenName());
    }

    private KeycloakSecurityContext getKeycloakSecurityContext() {
        return (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
    }
}

三、验证效果

启动Spring Boot 项目

from clipboard

我们先访问首页(未启动身份认证)

from clipboard

接下来,我们点Browse Books(需要登录)

from clipboard

自动跳转到KeyCloak的登录地址去了,输入上面我们创建好用户名和密码后,最后验证成功后会自动跳转到之前请求的页面,如下图:

from clipboard

本例源码:https://github.com/lanhusoft/Keycloak-Demo

四、总结

这就快速的实现了一个单点登录,统一用户中心方案,以后如果还有其它应用需要登录认证,也可以通过这个方式加入进来,不用每个系统都做一遍登录,认证,授权这些。

如果不采用单点登录SSO,统一用户中心方案会有以下弊端:

1、账号数据也分布在各个系统,对以后的系统集成,数据交互带来极大的难度。

2、每个系统都要做一个登录,身份认证,权限判断,带来多余的重复工作和增加维护难度。

3、用户可能需要记住不同系统的登录账号,用户体验差。

KeyCloak是开源,配置简单,开箱即用,后台统一管理,非常优秀,非常强大。当然你也可以通过自己往KeyCloak导已有用户和开启用户注册功能,这样就不用在后台人工创建用户。

下面是KeyCloak表结构,你完全可以自己扩展和自定义,做一个符合你们自己的统一用户认证中心。

from clipboard

PS:

KeyCloak官网:

https://www.keycloak.org/

https://github.com/keycloak/keycloak


参考:

https://www.thomasvitale.com/spring-security-keycloak/




小编蓝狐