Apache Camel 2.14 Rest DSL安全性 [英] Apache Camel 2.14 Rest DSL Security
问题描述
我想在Apache Camel 2.14中使用新的Rest DSL创建一个Rest接口.我想使用Jetty组件,并且有一个基本的示例设置,如下所示:
I would like to use the new Rest DSL in Apache Camel 2.14 to create a rest interface. I would like to use the Jetty component and I have a basic example setup like this:
Spring Security配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:spring-security="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
http://camel.apache.org/schema/spring-security http://camel.apache.org/schema/spring-security/camel-spring-security.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
<spring-security:http auto-config="true" use-expressions="true" >
<spring-security:intercept-url pattern="/**" access="isFullyAuthenticated()"/>
<spring-security:http-basic></spring-security:http-basic>
</spring-security:http>
<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="true"/>
<property name="decisionVoters">
<list>
<bean class="org.springframework.security.access.vote.RoleVoter"/>
</list>
</property>
</bean>
<spring-security:authentication-manager alias="authenticationManager">
<spring-security:authentication-provider user-service-ref="userDetailsService"/>
</spring-security:authentication-manager>
<spring-security:user-service id="userDetailsService">
<spring-security:user name="admin" password="admin" authorities="ROLE_USER, ROLE_ADMIN"/>
<spring-security:user name="user" password="user" authorities="ROLE_USER"/>
</spring-security:user-service>
<authorizationPolicy id="admin" access="ROLE_ADMIN"
authenticationManager="authenticationManager"
accessDecisionManager="accessDecisionManager"
xmlns="http://camel.apache.org/schema/spring-security"/>
<authorizationPolicy id="user" access="ROLE_USER"
xmlns="http://camel.apache.org/schema/spring-security"/>
骆驼路线配置
restConfiguration().component("jetty").host("0.0.0.0").port(24999).bindingMode(RestBindingMode.json).dataFormatProperty("prettyPrint", "true");
rest("address").description("Contains services for addresses").
consumes("application/json").
produces("application/json").
get().
route().policy("admin").
to("bean:restAddressApi?method=queryAddress").endRest();
当我尝试使用wget与此访问此受保护的URL时:
When I try to access this protected URL using wget with this:
wget --http-user=admin --http-password=admin http://localhost:24999/address/
然后我在控制台中收到此错误:
Then I get this error in the console:
org.apache.camel.CamelAuthorizationException: Cannot find the Authentication instance.. Exchange[Message: [Body is null]]
at org.apache.camel.component.spring.security.SpringSecurityAuthorizationPolicy.beforeProcess(SpringSecurityAuthorizationPolicy.java:72)
at org.apache.camel.component.spring.security.SpringSecurityAuthorizationPolicy$AuthorizeDelegateProcess.process(SpringSecurityAuthorizationPolicy.java:120)
at org.apache.camel.util.AsyncProcessorConverterHelper$ProcessorToAsyncProcessorBridge.process(AsyncProcessorConverterHelper.java:61)
at org.apache.camel.processor.RedeliveryErrorHandler.process(RedeliveryErrorHandler.java:416)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:191)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:118)
at org.apache.camel.processor.Pipeline.process(Pipeline.java:80)
at org.apache.camel.processor.CamelInternalProcessor.process(CamelInternalProcessor.java:191)
at org.apache.camel.component.jetty.CamelContinuationServlet.service(CamelContinuationServlet.java:150)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:668)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:684)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:503)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1086)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:429)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1020)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
at org.eclipse.jetty.server.handler.ResourceHandler.handle(ResourceHandler.java:406)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
at org.eclipse.jetty.server.Server.handle(Server.java:370)
at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:494)
at org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:971)
at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:1033)
at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:644)
at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235)
at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:82)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:696)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:53)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
at java.lang.Thread.run(Thread.java:745)
要使此功能正常工作,我在配置中缺少什么?
What am I missing in my config to get this working?
推荐答案
以下是我使用Apache Camel 2.15.2解决方案的方法.我将此后端与AngularJS前端一起使用,在其中使用httpInterceptor添加身份验证标头. 我使用JWT在授权标头中存储一个tenantId.该tenantId与Hibernate及其多租户支持一起使用.
Here is how I solved it using Apache Camel 2.15.2. I use this backend together with an AngularJS frontent where I use an httpInterceptor to add the authenitcation header. I use JWT to store a tenantId in the authorization header. This tenantId is used together with Hibernate and its Multi Tenancy Support.
使用AngularJs并以TypeScript编写的前端登录控制器:
Front end login controller, using AngularJs and written in TypeScript:
/// <reference path="../reference.ts"/>
module Controllers {
export class Credentials {
username:string;
password:string;
}
export interface ILoginControllerScope extends ng.IScope {
credentials:Credentials;
login:()=>void;
}
export class LoginController {
private _scope:ILoginControllerScope;
static $inject = ['$scope', '$http', 'UserService', '$location'];
constructor($scope:ILoginControllerScope, $http:ng.IHttpService, UserService:UserService, $location:ng.ILocationService) {
this._scope = $scope;
$scope.credentials = new Credentials();
$scope.login = ()=> {
$http.post('/api/authenticate', $scope.credentials)
.success(function (data:string, status:any, headers:any, config:any) {
// Remove all " characters from string.
data = data.replace(/"/gi, '');
UserService.setSecurityToken(data);
$location.path('/');
})
.error(function (data:any, status:any, headers:any, config:any) {
});
}
}
}
}
@Override
public void process(Exchange exchange) throws Exception {
Map<String, String> credentials = exchange.getIn().getBody(HashMap.class);
String username = credentials.get("username");
String password = credentials.get("password");
// Login logic that returns object() containing user info
exchange.getIn().setBody(jwtService.generateToken(tenantConfiguration));
}
}
}
这是负责创建JWT令牌的JwtService类:
This is the class JwtService that is responsible for creating a JWT Token:
package com.me.services;
import com.me.TenantConfiguration;
import com.google.common.collect.Maps;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.security.SecureRandom;
import java.util.Map;
import java.util.Random;
@Service
public class JwtService {
public static final String TENANT_ID = "tenant_id";
public static final String ROLE = "role";
@Value("${jwt.subject}")
private String jwtSubject;
private byte[] signingKey;
private SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
public SignatureAlgorithm getSignatureAlgorithm() {
return signatureAlgorithm;
}
public byte[] getSigningKey() {
if (this.signingKey == null) {
// Generate new signingKey
Random random = new SecureRandom();
signingKey = new byte[64];
random.nextBytes(signingKey);
}
return this.signingKey;
}
public String getJwtSubject() {
return jwtSubject;
}
public String generateToken(TenantConfiguration tenantConfiguration){
Map<String, Object> claims = Maps.newHashMap();
claims.put(TENANT_ID, tenantConfiguration.getSessionId());
String token = Jwts.builder().setSubject(this.getJwtSubject()).setClaims(claims).signWith(this.getSignatureAlgorithm(), this.getSigningKey()).compact();
return token;
}
}
要在路由中使用它,我会这样定义它:
To use this in a route I define it like this:
rest("api/messages").description("Restful API for messages").
get().id("GetMessages").route().to("springSecurityContextLoader").policy(authorizationPolicy).to("bean:restMessageApi?method=getAllMessages").endRest().
SpringSecurityContextloader
SpringSecurityContextloader
@Service
public class SpringSecurityContextLoader implements Processor {
@Inject
private JwtService jwtService;
@Override
public void process(Exchange exchange) throws Exception {
String authorization = exchange.getIn().getHeader("Authorization", String.class);
Jwt jwt = Jwts.parser().setSigningKey(jwtService.getSigningKey()).parse(authorization);
Map<String, Object> claims = (Map<String, Object>) jwt.getBody();
String tenantId = claims.get(JwtService.TENANT_ID).toString();
Authentication authentication = new PreAuthenticatedAuthenticationToken(tenantId, "something", Lists.newArrayList(new SimpleGrantedAuthority("ROLE_USER")));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
这篇关于Apache Camel 2.14 Rest DSL安全性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!