问题描述
我正在尝试使用基于 CXF 的soap 服务安全地安装spring boot 应用程序。 我已经浏览了一千个论坛,但无法让它发挥作用。
我做了不同的测试,我在下面评论:
-
如果我仅在具有基本身份验证的 cxf-rt-ws-security 级别启用安全性,则该应用程序可以正常工作,并且例如从 SOAPUI 调用时,它会验证基本身份验证 http 凭据。这里我为 cxf BasicAuthAuthorizationInterceptor 定义了一个拦截器
<dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-spring-boot-starter-jaxws</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-ws-security</artifactId> <version>3.4.0</version> </dependency>
package com.byl.arquetipo.config;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.cxf.binding.soap.interceptor.soapHeaderInterceptor;
import org.apache.cxf.configuration.security.AuthorizationPolicy;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.Message;
import org.apache.cxf.transport.Conduit;
import org.apache.cxf.ws.addressing.EndpointReferenceType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BasicAuthAuthorizationInterceptor extends SoapHeaderInterceptor {
/** La constante logger. */
private static final Logger logger = LoggerFactory.getLogger(BasicAuthAuthorizationInterceptor.class);
/** La variable users. */
Map<String,String> users = new HashMap<String,String>();
/**
* Obtiene users.
*
* @return users
*/
public Map<String,String> getUsers() {
return users;
}
/**
* Establece users.
*
* @param users
* users
*/
public void setUsers(Map<String,String> users) {
this.users = users;
}
/**
* {@inheritDoc}
*/
@Override
public void handleMessage(Message message) throws Fault {
AuthorizationPolicy policy = message.get(AuthorizationPolicy.class);
// If the policy is not set,the user did not specify credentials.
// 401 is sent to the client to indicate that authentication is required.
if (policy == null) {
sendErrorResponse(message,HttpURLConnection.HTTP_UNAUTHORIZED);
return;
}
String username = policy.getUserName();
String password = policy.getpassword();
// CHECK USERNAME AND PASSWORD
if (!checkLogin(username,password)) {
logger.error("handleMessage: Invalid username or password for user: " + policy.getUserName());
sendErrorResponse(message,HttpURLConnection.HTTP_FORBIDDEN);
}
}
/**
* Check login.
*
* @param username
* username
* @param password
* password
* @return <code>true</code>,si termina correctamente
*/
private boolean checkLogin(String username,String password) {
for (Map.Entry<String,String> item : users.entrySet()) {
if (item.getKey().equals(username) && password.equals(item.getValue())) {
return true;
}
}
return false;
}
/**
* Send error response.
*
* @param message
* message
* @param responseCode
* response code
*/
private void sendErrorResponse(Message message,int responseCode) {
Message outMessage = getoutMessage(message);
outMessage.put(Message.RESPONSE_CODE,responseCode);
// Set the response headers
@SuppressWarnings("unchecked")
Map<String,List<String>> responseHeaders = (Map<String,List<String>>) message.get(Message.PROTOCOL_HEADERS);
if (responseHeaders != null) {
responseHeaders.put("WWW-Authenticate",Arrays.asList(new String[] { "Basic realm=realm" }));
responseHeaders.put("Content-Length",Arrays.asList(new String[] { "0" }));
}
message.getInterceptorChain().abort();
try {
getConduit(message).prepare(outMessage);
close(outMessage);
} catch (IOException e) {
e.printstacktrace();
}
}
/**
* Obtiene out message.
*
* @param inMessage
* in message
* @return out message
*/
private Message getoutMessage(Message inMessage) {
Exchange exchange = inMessage.getExchange();
Message outMessage = exchange.getoutMessage();
if (outMessage == null) {
Endpoint endpoint = exchange.get(Endpoint.class);
outMessage = endpoint.getBinding().createMessage();
exchange.setoutMessage(outMessage);
}
outMessage.putAll(inMessage);
return outMessage;
}
/**
* Obtiene conduit.
*
* @param inMessage
* in message
* @return conduit
* @throws IOException
* Signals that an I/O exception has occurred.
*/
private Conduit getConduit(Message inMessage) throws IOException {
Exchange exchange = inMessage.getExchange();
EndpointReferenceType target = exchange.get(EndpointReferenceType.class);
// Conduit conduit = exchange.getDestination().getBackChannel(inMessage,null,target);
Conduit conduit = exchange.getDestination().getBackChannel(inMessage);
exchange.setConduit(conduit);
return conduit;
}
/**
* Close.
*
* @param outMessage
* out message
* @throws IOException
* Signals that an I/O exception has occurred.
*/
private void close(Message outMessage) throws IOException {
OutputStream os = outMessage.getContent(OutputStream.class);
os.flush();
os.close();
}
}
- 如果我此时启用 spring-boot-starter-security,它总是给我一个 401,我无法让它工作。同样通过启用 spring-boot-starter-security 我无法调试它进入 BasicAuthAuthorizationInterceptor 拦截器
'
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
我必须为应用程序的其余部分启用 spring-boot-starter-security,因为我还为 spring 公开了 REST 服务。
有人可以指导我如何解决问题吗?
解决方法
如果您添加 Spring Security,您可能希望利用其处理基本身份验证的功能(并且不再使用您的拦截器)。
所以添加如下内容:
@EnableWebSecurity
public class BasicSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.antMatchers("/services/*").hasRole("MEMBER")
.antMatchers("/").permitAll()
)
.httpBasic().realmName("Your realmname")
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
...
}
有关详细信息,请参阅 https://docs.spring.io/spring-boot/docs/2.4.5/reference/htmlsingle/#boot-features-security。