Keycloak使用说明(Java Spring Boot)

 

第一步:在keycloak平台上,新建一个client app

 联系Keycloak管理员,提供 应用的 root url 和 app name即可。

 建好client之后,可以得到一个 client secret(密匙)。

 

第二步:在client project中 加入 keycloak配置

配置形如:

# 空间名,默认所有app和用户都在一个keycloak空间

keycloak.realm=ops

# keycloak服务器的auth地址

keycloak.auth-server-url=http://localhost:8180/auth

# client app name

keycloak.resource=fm-cache-cloud

# client secret

keycloak.credentials.secret=d4589683-0ce7-4982-bcd3-c48a12572f79

# 登录url和所需要的role

keycloak.securityConstraints[0].authRoles[0] = user

keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /manage/ssologin/*

上面的配置,除了 最后一项,其他都是基本配置,直接填好就行了。

最后一项需要先在controller中定义这样一个用于登录的url,下面会讲。

 

在讲第三步之前,说一下Keycloak客户端接入原理:

(keycloak支持很多种方式接入,我只讲其中一部分)

基本原理

    keycloak是JBOSS开源的,JBOSS是做服务器的,所以,对于服务器,它比谁都玩得熟,Keycloak的强大之处也在于,它对于客户端应用的管控,直接可以到 服务器层面(相当于给服务器装一个插件,然后进入这个服务器的请求,都会被拦截和认证)。

    本文以我们常用的Spring Boot 内嵌的 Tomcat 服务器 为例,在项目中引入 keycloak包并配置好之后,实际上开启了一个 tomcat的 filter,这个filter会拦截指定的url,如果没登录,就跳转到统一登录页面进行登录。

    (如果不是用的Spring boot内嵌的tomcat服务器,比如用的是独立的tomcat服务器,原理也是一样的,只是配置方法不一样)

    keycloak提供了很多种插件(adapter),例如仅Java的adapter就有如下:

        2.1.1. Java Adapter Config

        2.1.2. JBoss EAP/WildFly Adapter

        2.1.4. JBoss Fuse 6、7 Adapter

        2.1.6. Spring Boot Adapter

        2.1.7. Tomcat 6, 7 and 8 Adapters

        2.1.8. Jetty 9.x Adapters

        2.1.9. Jetty 8.1.x Adapter

        2.1.10. Spring Security Adapter

        2.1.11. Java Servlet Filter Adapter

    

    下面以 Spring Boot Adapter为例,说明如何装插件。其他服务器,或者其他语言的客户端是类似的,很简单。

安装方法:

例如 spring boot 1.x,在pom.xml中引入下面依赖即可:

<!-- Keycloak  -->

<dependency>

    <groupId>org.keycloak</groupId>

    <artifactId>keycloak-legacy-spring-boot-starter</artifactId>

    <version>5.0.0</version>

</dependency>

如果是spring boot 2.x版本,上面的 starter 换成 keycloak-spring-boot-starter。

 

由于keycloak是基于 filter拦截器的,所以如果 项目本身 已经用了filter来作为登录控制的话,则需要进行改造,Java项目常见情况如下:

1、基于shiro框架进行登录控制;

2、基于spring security进行登录控制;

3、基于自定义简单的servlet filter进行登录控制;

下面,针对 2、3 项目情况,说明如何进行集成配置(注意,不同的项目,情况可能不完全一样,只要掌握思路即可)。

 

第三步(针对“3、基于简单servlet filter登录的项目”)

改造之前:

  • 原项目,采用了filter来拦截请求,如果没登录,则跳转到登录页面(比如 /mange/login)。

  • 使用项目自带的登录页面,进行登录。

 

改造之后:

  • 沿用原来的filter,但是如果没登录,则跳转到 用于统一登录的指定controller(比如 /mange/ssologin);

  • 把这个统一登录的controller的url,配置成 keycloak拦截的登录地址,使用keycloak来进行登录;

 

这个controller,逻辑很简单,一个例子如下,流程见注释:

@GetMapping("/ssologin")

public View ssologin(HttpServletRequest request) {

    // 1、从request获取用户名,再查看本系统中有无此用户

    // 2、有这个用户,则执行登录成功逻辑,代表登录成功

    // 3、没有这个用户,则执行登录失败逻辑,比如跳转到登录页面

}

一个真实例子:

@RequestMapping(value = "/ssologin", method = RequestMethod.GET)

public ModelAndView ssologin(HttpServletRequest request, HttpServletResponse response) {

    // 从request获取用户名,再查看本系统中有无此用户

    String userName = Identity(request).getName();

    AppUser user = userService.getByName(userName);

    if (user == null) {

        return new ModelAndView("redirect:/manage/login");

    else {

        // 有这个用户,则添加到session或者cookie中,代表登录成功

        userLoginService.addLoginStatus(request, response, user.getId().toString());

    }

    // 返回用户主页

    return new ModelAndView("redirect:/admin/app/list.do");

}

 

登录原理 说明:

由于在keycloak配置中加入了url权限控制,如下

# 登录url和所需要的role

keycloak...authRoles[0] = user

keycloak...patterns[0] = /manage/ssologin/*

那么,访问这个 url,在没登录的情况下,就会跳转到 统一登录页面,用户输入用户密码成功登录之后,就会进入到上面定义的 controller中,再执行应用本地的登录逻辑即可。

 

退出登录,很简单,只需要执行  HttpServletRequest.logout() 即可

例如:

@GetMapping(value = "/logout")

public void logout(HttpServletRequest request) throws ServletException {

    // 先移除本地的session或者cookie

    userLoginService.removeLoginStatus(request, response);

    // 然后执行 request.logout() 即可

    request.logout();

}

 

第三步(针对“2、基于spring security进行登录的项目”)

 

改造之前:

  • 请求被spring security的UsernamePasswordAuthenticationFilter拦截,判断是否登录,如果未登录,则跳转到项目自己的登录页面。

  • 使用项目自带的登录页面,进行登录。

 

准备工作:写一个KeycloakAuthenticationFilter,重载spring security的UsernamePasswordAuthenticationFilter,它的逻辑是,先判断有没有用户进行统一登录,如果用户已经统一登录了,但是本地没登录,则进行本地登录。

 

改造之后:

  • 请求被spring security的KeycloakAuthenticationFilter拦截,判断是否登录,如果未登录,则跳转到 用于统一登录的指定controller(比如 /keycloak/ssologin);

  • 把这个统一登录的controller的url,配置成 keycloak拦截的登录地址,使用keycloak来进行登录;

  • keycloak登录的controller执行成功之后,再跳转到spring security的登录处理url进行登录。

 

改造之前,spring security配置如下

  @Override

  protected void configure(HttpSecurity http) throws Exception {

    http.csrf().disable();

    http.headers().frameOptions().disable();

    http.authorizeRequests().antMatchers("/openapi/**""/keycloak/**""/img/**")

    .permitAll().antMatchers("/**").hasAnyRole(USER_ROLE);

    http.formLogin().loginPage("/signin").permitAll().loginProcessingUrl("/sslogin")

        .failureUrl("/signin?#/error").and().httpBasic();

    http.logout().invalidateHttpSession(true).clearAuthentication(true)

        .logoutSuccessUrl("/signin?#/logout");

    http.exceptionHandling().authenticationEntryPoint(

        new LoginUrlAuthenticationEntryPoint("/signin?#/logout"));

  }

改造之后,spring security配置如下:

  @Override

  protected void configure(HttpSecurity http) throws Exception {

    http.csrf().disable();

    http.headers().frameOptions().disable();

    http.authorizeRequests().antMatchers("/openapi/**""/keycloak/**""/img/**")

    .permitAll().antMatchers("/**").hasAnyRole(USER_ROLE);

    http.formLogin().loginPage("/signin").permitAll().loginProcessingUrl("/sslogin")

        .failureUrl("/signin?#/error").and().httpBasic().and()

        // add by zollty

        .addFilterBefore(keycloakAuthenticationFilter(), BasicAuthenticationFilter.class);

    http.logout().invalidateHttpSession(true).clearAuthentication(true)

        .logoutSuccessUrl("/signin?#/logout")

        // add by zollty

        .addLogoutHandler(new KeycloakSpringLogoutHandler());

    http.exceptionHandling().authenticationEntryPoint(

        // to keycloak ssologin controller

        new LoginUrlAuthenticationEntryPoint("/keycloak/ssologin"));

  }

即,加了一个自定义的 keycloakAuthenticationFilter 和 KeycloakSpringLogoutHandler,同时 将LoginUrlAuthenticationEntryPoint登录地址,修改成 用于统一登录的指定controller的URL。这个controller代码如下:

@RequestMapping(value = "keycloak/ssologin", method = RequestMethod.GET)

public ModelAndView ssologin() {

 

    return new ModelAndView("redirect:/sslogin");

}

进入到这个方法,代表已经sso登录成功,然后直接跳转到 spring security的loginProcessingUrl进行本地登录即可。

KeycloakSpringLogoutHandler的代码如下:

public class KeycloakSpringLogoutHandler implements LogoutHandler {

 

 

    @Override

    public void logout(HttpServletRequest request, HttpServletResponse response

                        , Authentication authentication) {

         

        // 退出keycloak sso

        try {

            request.logout();

        catch (ServletException e) {

            e.printStackTrace();

        }

         

    }

 

}

其作用是退出统一登录。

KeycloakAuthenticationFilter的代码如下:

public class KeycloakAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

     

    static final String DEFAULT_PASSWD_SIGN = "`kc`";

 

    @Override

    public Authentication attemptAuthentication(HttpServletRequest request, 

            HttpServletResponse response) throws AuthenticationException {

      

        String username = obtainUsername(request);

        String password = null;

         

        Identity identity = new Identity(request);

        if (username == null && identity.getSecurityContext() != null) {

            username = identity.getName();

            password = DEFAULT_PASSWD_SIGN;

        else {

            password = obtainPassword(request);

            if (password == null) {

                password = "";

            else if(DEFAULT_PASSWD_SIGN.equals(password)) {

                throw new AuthenticationServiceException("Auth error");

            }

        }

         

 

        username = username.trim();

 

        UsernamePasswordAuthenticationToken authRequest = 

            new UsernamePasswordAuthenticationToken(username, password);

 

        setDetails(request, authRequest);

 

        return this.getAuthenticationManager().authenticate(authRequest);

    }

 

}

这个代码来源于spring security的UsernamePasswordAuthenticationFilter,只是加了7行代码,用于先判断是否有用户已经统一登录过,如果已经统一登录了,则将其密码设为一个特殊字符,这是一个取巧的做法,因为用户已经统一登录了,所以后面只要看到是这个特殊字符,就不再验证密码,直接登录。

 

其他编程语言应用的接入

 

方法一:自己根据OpenID Connect和OAuth2.0的原理,找到相应的client,引入自己的项目使用。

官方指导文档:https://www.keycloak.org/docs/latest/securing_apps/index.html#other-openid-connect-libraries

 

方法二:直接在网上或GitHub上搜索现成的第三方Client、Adapter或Demo,参照配置。例如:

1)Keycloak Golang相关的第三方adapter:

https://github.com/mitch-strong/KeycloakGo

https://github.com/cwocwo/keycloak-adapter-go

更多参见:https://github.com/search?l=Go&q=Keycloak&type=Repositories

2)Python相关的Client、Adapter或Demo:(包括Django、Flask相关的例子都有)

https://github.com/search?l=Python&q=Keycloak&type=Repositories

 

全文总结

    在有keycloak adapter的加持下,keycloak的接入相当简单,它是可以做到不改一行代码的 —— 之所以上面提到一些小的改动,完全是 为了 替换或者 适配 原来项目已有 的登录filter配置,把原来的账号+密码的登录形式,拦截并跳转到 keycloak统一登录,登录完成之后,再在本地项目进行登录。

    明白这个原理之后,其他类型的项目都是一样的处理逻辑。

    具体接入时,参见这个文档 securing apps guide,说得很清楚。

    另外,参考其GitHub上的 Quick-Start Demo

 

附 Keycloak官网: www.keycloak.org

 

Special专题:关于现代化前端、移动端的接入说明

    参见这篇文章:Keycloak现代化前端、移动端的接入说明

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值