CORS 问题 - 使用 Keycloak 保护 ReactJS 前端和 JAX-RS 后端
Posted
技术标签:
【中文标题】CORS 问题 - 使用 Keycloak 保护 ReactJS 前端和 JAX-RS 后端【英文标题】:CORS Problem - Protecting ReactJS Frontend and JAX-RS Backend with Keycloak 【发布时间】:2021-11-14 03:39:50 【问题描述】:我是一名来自德国的初级软件开发人员,我目前正在尝试从 Jax-RS Rest API(部署在 Wildfly 服务器上)设置一些 Web 服务,这些服务受“Keycloak”保护,可通过简单的 react 应用程序访问。
我已经完成了这篇文章中的所有确切步骤:https://medium.com/devops-dudes/secure-front-end-react-js-and-back-end-node-js-express-rest-api-with-keycloak-daf159f0a94e。
唯一的区别如下:
我有三项服务:“零级”、“一级”和“二级”。 每个服务都只返回一个字符串(例如:“This is the Service Level One”) 我在 Keycloak 中定义了两个角色:“一级用户”和“二级用户” 二级用户有权访问所有服务 一级用户只能被授权访问一级和零级服务 其他所有用户只能访问零级服务在我的 React 应用程序中,我有三个按钮,它们将通过框架“axios”访问服务。您单击一个按钮,如果您被授权,返回的字符串将在控制台中注销。
我的问题: 如果我运行我的应用程序,当我尝试以经过身份验证的一级或二级用户身份访问“LevelOne”或“LevelTwo”时,我的 Web 控制台中总是会出现 CORS 错误。不受 Keycloak 保护的零级服务不存在此问题。
我的(翻译后的)错误: 跨源请求被阻止:同源策略不允许对外部资源 URL_FROM_API_SERVICE 进行读取访问 - 原因:缺少 CORS 标头“Access-Control-Allow-Origin”!
我尝试了很多我在网上找到的东西 --> 我在我的 Rest API 中创建了一个 CORS 过滤器,我尝试在我的 keycloak.json 中放入“enable-cors:true”,我在我的 Keycloak 客户端配置中的“web-origins”字段。但没有任何效果。 :(
我做错了什么?我真的需要你的帮助!我对这一切都很陌生,非常感谢一些支持。
我的 Keycloak 配置与文章中显示的相同,只是名称不同。
在 keycloak.json 中添加“enable-cors:true”并将“web-origin”设置为 Keycloak 管理控制台上的正确来源也无济于事:(
API 和 React App 目前在 HTTP 上运行,而 Keycloak 在另一台具有自签名证书的机器上在 HTTPS 上运行。
这是我所有的代码:
React 应用中的我的 App.js:
import './App.css';
import Secured from './components/Secured.js'
import axios from 'axios';
var axiosInstance = axios.create(
baseURL: 'http://MY_BASE_URL/login-restapi/api/'
);
axiosInstance.interceptors.request.use(
config =>
const token = window.accessToken ? window.accessToken : 'dummy_token';
config.headers['Authorization'] = 'Bearer' + token;
return config;
,
error =>
Promise.reject(error)
);
axiosInstance.interceptors.response.use((response) =>
return response
, function (error)
return Promise.reject(error);
);
function App()
return (
<div className="App">
<Secured></Secured>
<button onClick=() =>
axiosInstance.get('/levelZero').then(res =>
console.log(res.data)
)
>LevelZero</button>
<button onClick=() =>
axiosInstance.get('/levelOne').then(res =>
console.log(res.data)
)
>LevelOne</button>
<button onClick=() =>
axiosInstance.get('/levelTwo').then(res =>
console.log(res.data)
)
>LevelTwo</button>
</div>
);
export default App;
我在 React 应用中的 Secured.js:
import React, Component from 'react';
import Keycloak from 'keycloak-js';
class Secured extends Component
constructor(props)
super(props);
this.state = keycloak: null, authenticated: false ;
componentDidMount()
const keycloak = Keycloak('/keycloak.json');
keycloak.init( onLoad: 'login-required' ).then(authenticated =>
this.setState( keycloak: keycloak, authenticated: true)
if (authenticated)
window.accessToken = keycloak.token;
)
render()
if (this.state.keycloak)
if(this.state.authenticated) return (
<div>
<p>You are now logged in :)</p>
</div>
); else return (<div>Unable to authenticate!</div>)
return (
<div>Initializing Keycloak...</div>
);
export default Secured;
React 应用中的我的 Keycloak.json:
"realm": "(MY_REALM_NAME)",
"auth-server-url": "MY_AUTHSERVER_URL",
"ssl-required": "none",
"resource": "react-web-app",
"public-client": true,
"verify-token-audience": true,
"use-resource-role-mappings": true,
"confidential-port": 0
我的零级服务:
@Path("/levelZero")
public class LevelZeroResource
@GET
@Produces("text/plain")
public String levelZero()
return "Everybody can access this.";
我的一级服务:
@Path("/levelOne")
public class LevelOneResource
@GET
@Produces("text/plain")
public String levelOne()
return "You need to be at least a Level One User to access this.";
我的二级服务:
@Path("/levelTwo")
public class LevelTwoResource
@GET
@Produces("text/plain")
public String levelTwo()
return "You need to be a LevelTwo-user to access this.";
我的来自 Rest API 的 CORS 过滤器:
@Provider
public class CORSFilter implements ContainerResponseFilter
@Override
public void filter(final ContainerRequestContext requestContext,
final ContainerResponseContext cres) throws IOException
cres.getHeaders().add("Access-Control-Allow-Origin", "http://URL_FROM_REACT_APP");
cres.getHeaders().add("Access-Control-Allow-Headers", "*");
cres.getHeaders().add("Access-Control-Allow-Credentials", "*");
cres.getHeaders().add("Access-Control-Allow-Methods", "*");
cres.getHeaders().add("Access-Control-Max-Age", "1209600");
我的来自 Rest API 的 Keycloak.json:
"realm": "MY_REALM_NAME",
"bearer-only": true,
"enable-cors": true,
"auth-server-url": "https://MY_AUTH_SERVER_URL",
"ssl-required": "none",
"resource": "login-restapi",
"verify-token-audience": true,
"use-resource-role-mappings": true,
"confidential-port": 0
我的来自 Rest API 的 web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<module-name>login-restapi</module-name>
<security-constraint>
<web-resource-collection>
<web-resource-name>LevelOneResource</web-resource-name>
<url-pattern>/api/levelOne</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>levelOneRole</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>LevelTwoResource</web-resource-name>
<url-pattern>/api/levelTwo</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>levelTwoRole</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>KEYCLOAK</auth-method>
<realm-name>MY_REALM_NAME</realm-name>
</login-config>
<security-role>
<role-name>levelOneRole</role-name>
</security-role>
<security-role>
<role-name>levelTwoRole</role-name>
</security-role>
</web-app>
【问题讨论】:
【参考方案1】:您不需要添加自定义过滤器。
只需将enable-cors
设置为true
(就像您所做的那样)并确保在Keycloak 中正确配置了Web origins
。您需要将 React 前端的来源添加到 Keycloak 客户端配置中的 Web origins
列表中。
Keycloak 然后会将这些来源编码为您的访问令牌内的有效来源。 JAX-RS 应用程序中的 Keycloak 适配器将解释它们并根据需要返回 Access-Control-Allow-Origin
标头。
【讨论】:
感谢您的回复!不幸的是,这并没有帮助:(到目前为止没有任何改变。另外:React App 和 API 目前使用 HTTP 运行,而 Keycloak 服务器使用自签名证书在 HTTPS 上运行 --> 是否有潜在的问题有可能吗?在实际的 CORS 错误之前,我还会收到另一个错误:“401 Unauthorized, Referrer Policy: strict origin when cross origin”即使授权令牌已分配给请求并且用户具有正确的角色...跨度> 【参考方案2】:我看到你已经在你的 keycloak 配置文件 "enable-cors: true
" 中启用了 cors,所以请确保 keycloak 上的 web 来源字段(如 @sventorben 所示)也有后端 url。
因为即使keycloak-js
对用户进行身份验证,后端也会进行授权,这意味着也与 keycloak 对话。
【讨论】:
我现在试过了,但仍然没有任何改变:( 您在 login-restapi 或 react-web-app 中进行了更改? 显然我只能更改 react-web-app,只要 login-restapi 的访问类型是“仅承载”-> 客户端配置中不存在 web origins 字段然后. 你必须删除 enable-cors 然后 你的意思是在 react-web-app 或 login-rest-api 的 keycloak.json 中?以上是关于CORS 问题 - 使用 Keycloak 保护 ReactJS 前端和 JAX-RS 后端的主要内容,如果未能解决你的问题,请参考以下文章