龙空技术网

仅十分钟即可接入Spring Boot/Vue前后端分离应用实现SSO单点登录

Java技术那些事 1440

前言:

目前咱们对“spring访问js”都比较关注,同学们都需要知道一些“spring访问js”的相关知识。那么小编在网上收集了一些有关“spring访问js””的相关文章,希望咱们能喜欢,大家快快来学习一下吧!

登录及身份认证是现代web应用最基本的功能之一,对于企业内部的系统,多个系统往往希望有一套SSO服务对企业用户的登录及身份认证进行统一的管理,提升用户同时使用多个系统的体验,Keycloak正是为此种场景而生。本文将简明的介绍Keycloak的安装、使用,并给出目前较流行的前后端分离应用如何快速接入Keycloak的示例。

Keycloak是什么

Keycloak是一种面向现代应用和服务的开源IAM(身份识别与访问管理)解决方案

Keycloak提供了单点登录(SSO)功能,支持OpenID Connect、OAuth 2.0、SAML 2.0标准协议,拥有简单易用的管理控制台,并提供对LDAP、Active Directory以及Github、Google等社交账号登录的支持,做到了非常简单的开箱即用。

Keycloak常用核心概念介绍

首先通过官方的一张图来了解下整体的核心概念

这里先只介绍4个最常用的核心概念:

Users: 用户,使用并需要登录系统的对象Roles: 角色,用来对用户的权限进行管理Clients: 客户端,需要接入Keycloak并被Keycloak保护的应用和服务Realms: 领域,领域管理着一批用户、证书、角色、组等,一个用户只能属于并且能登陆到一个域,域之间是互相独立隔离的, 一个域只能管理它下面所属的用户Keycloak服务安装及配置安装Keycloak

Keycloak安装有多种方式,这里使用Docker进行快速安装

docker run -d --name keycloak \    -p 8080:8080 \    -e KEYCLOAK_USER=admin \    -e KEYCLOAK_PASSWORD=admin \    jboss/keycloak:10.0.0

访问并点击Administration Console进行登录

创建Realm

创建一个新的realm: demo,后续所有的客户端、用户、角色等都在此realm中创建

创建客户端创建前端应用客户端

创建一个新的客户端:vue-demo,Access Type选择public

创建后端应用客户端

创建一个新的客户端:spring-boot-demo,Access Type选择bearer-only

保存之后,会出现Credentials的Tab,记录下这里的secret,后面要用到

关于客户端的访问类型(Access Type)

上面创建的2个客户端的访问类型分别是public、bearer-only,那么为什么分别选择这种类型,实际不同的访问类型有什么区别呢?

事实上,Keycloak目前的访问类型共有3种:

confidential:适用于服务端应用,且需要浏览器登录以及需要通过密钥获取access token的场景。典型的使用场景就是服务端渲染的web系统。

public:适用于客户端应用,且需要浏览器登录的场景。典型的使用场景就是前端web系统,包括采用vue、react实现的前端项目等。

bearer-only:适用于服务端应用,不需要浏览器登录,只允许使用bearer token请求的场景。典型的使用场景就是restful api。

创建用户和角色创建角色

创建2个角色:ROLE_ADMIN、ROLE_CUSTOMER

创建用户

创建2个用户:admin、customer

绑定用户和角色给admin用户分配角色ROLE_ADMIN给customer用户分配角色ROLE_CUSTOMERVue应用集成Keycloak简明指南创建vue项目

vue create vue-demo
添加官方Keycloak js适配器
npm i keycloak-js --savenpm i axios --save
main.js
import Vue from 'vue'import App from './App.vue'import Keycloak from 'keycloak-js'Vue.config.productionTip = false// keycloak init optionsconst initOptions = {  url: ';,  realm: 'demo',  clientId: 'vue-demo',  onLoad:'login-required'}const keycloak = Keycloak(initOptions)keycloak.init({ onLoad: initOptions.onLoad, promiseType: 'native' }).then((authenticated) =>{  if(!authenticated) {    window.location.reload();  } else {    Vue.prototype.$keycloak = keycloak    console.log('Authenticated')  }  new Vue({    render: h => h(App),  }).$mount('#app')  setInterval(() =>{    keycloak.updateToken(70).then((refreshed)=>{      if (refreshed) {        console.log('Token refreshed');      } else {        console.log('Token not refreshed, valid for '            + Math.round(keycloak.tokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds');      }    }).catch(error => {      console.log('Failed to refresh token', error)    })  }, 60000)}).catch(error => {  console.log('Authenticated Failed', error)})
HelloWorld.vue
<template>  <div class="hello">    <h1>{{ msg }}</h1>    <div>      <p>        current user: {{user}}      </p>      <p>        roles: {{roles}}      </p>      <p>        {{adminMsg}}      </p>      <p>        {{customerMsg}}      </p>    </div>  </div></template><script>import axios from 'axios'export default {  name: 'HelloWorld',  props: {    msg: String  },  data() {    return {      user: '',      roles: [],      adminMsg: '',      customerMsg: ''    }  },  created() {    this.user = this.$keycloak.idTokenParsed.preferred_username    this.roles = this.$keycloak.realmAccess.roles    this.getAdmin()            .then(response=>{              this.adminMsg = response.data            })            .catch(error => {              console.log(error)            })    this.getCustomer()            .then(response => {              this.customerMsg = response.data            })            .catch(error => {              console.log(error)            })  },  methods: {    getAdmin() {      return axios({        method: 'get',        url: ';,        headers: {'Authorization': 'Bearer ' + this.$keycloak.token}      })    },    getCustomer() {      return axios({        method: 'get',        url: ';,        headers: {'Authorization': 'Bearer ' + this.$keycloak.token}      })    }  }}</script>

getAdmin()及getCustomer()这2个方法内部分别请求restful api

Spring Boot应用集成Keycloak简明指南添加Keycloak Maven依赖

<dependency>    <groupId>org.keycloak</groupId>    <artifactId>keycloak-spring-boot-starter</artifactId>    <version>10.0.0</version></dependency>
Spring Boot配置文件

官方文档及网上大部分示例使用的都是properties格式的配置文件,而yaml格式的配置文件相对更简洁清晰些,此示例使用yaml格式的配置文件,内容如下

server:  port: 8082keycloak:  realm: demo  auth-server-url:   resource: spring-boot-demo  ssl-required: external  credentials:    secret: 2d2ab498-7af9-48c0-89a3-5eec929e462b  bearer-only: true  use-resource-role-mappings: false  cors: true  security-constraints:    - authRoles:        - ROLE_CUSTOMER      securityCollections:        - name: customer          patterns:            - /customer    - authRoles:        - ROLE_ADMIN      securityCollections:        - name: admin          patterns:            - /admin

除了几个必填的配置项外,另外需要注意的几个配置项如下

credentials.secret:上文添加客户端后Credentials Tab内对应的内容

bearer-only:设置为true,表示此应用的Keycloak访问类型是bearer-only

cors:设置为true表示允许跨域访问

security-constraints:主要是针对不同的路径定义角色以达到权限管理的目的

/customer:只允许拥有ROLE_CUSTOMER角色的用户才能访问/admin:只允许拥有ROLE_ADMIN角色的用户才能访问未配置的路径表示公开访问Controller内容

@RestControllerpublic class HomeController {    @RequestMapping("/")    public String index() {        return "index";    }    @RequestMapping("/customer")    public String customer() {        return "only customer can see";    }    @RequestMapping("/admin")    public String admin() {        return "only admin cas see";    }}
项目效果演示

分别启动前后端项目后,本地8081端口对应vue前端项目,本地8082端口对应Spring Boot实现的restful api项目

首次访问vue前端项目

第一次访问vue项目会跳转Keycloak登录页

登录admin用户登录customer用户总结

Keycloak部署及接入简单,轻量的同时功能又不失强大,非常适合企业内部的SSO方案。

标签: #spring访问js #vue3 keycloak #react keycloak