【发布时间】: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>
【问题讨论】:
标签: javascript reactjs cors jax-rs keycloak