最近接到的需求,要求隐藏服务请求列表清单,增加安全性。
1. 方法一:直接隐藏服务列表
经过翻阅相关资料和源码,最终发现CXF内置的支持——通过设置hide-service-list-page
的Servlet初始化参数即可达到目的(该参数的读取和生效是在ServletController
类中完成的)。
具体的配置方式如下(根据具体的环境任选其一):
- web.xml
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<init-param>
<!-- 隐藏服务列表 -->
<param-name>hide-service-list-page</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
- SpringBoot
# 直接在application.properties中配置如下键值对即可
cxf.servlet.init.hide-service-list-page=true
- Java代码(SpringBoot)
@Configuration
public class SelfConfigurer implements ServletContextInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// 通过实现 ServletContextInitializer 接口直接注册Servlet
// 因为我们需要设置 InitParameter, 所以不能直接使用ServletRegistrationBean
final Dynamic servletDynamic = servletContext.addServlet("CXFServlet", CXFServlet.class);
servletDynamic.addMapping("/ws/*");
// 隐藏服务列表
//servletDynamic.setInitParameter("hide-service-list-page", "true");
}
}
2. 方法二:需要授权才能访问服务列表
在查找本文标题的解决方案时候,偶然在ServletController.init()
的方法中发现了两个有趣的配置项service-list-page-authenticate
和service-list-page-authenticate-realm
。这里笔者就不多赘述其中的探究过程了,直接给出相应的配置项和实现步骤吧。
service-list-page-authenticate
配置项的配置方法和上面的hide-service-list-page
相同。这里需要一点注意的就只有service-list-page-authenticate-realm
了。
service-list-page-authenticate-realm
配置项
先看相关的配置文件
cxf.servlet.init.service-list-page-authenticate=true
cxf.servlet.init.service-list-page-authenticate-realm=fulizhe
所以这里的重点就成了fulizhe指代的内容。具体的原因参见下面的参考链接一,笔者这里只给出解决方案。毕竟能找到这里来的是为了解决问题的。
- 在项目根目录创建名为
fulizhe.config
的文件,内容如下:
fulizhe {
com.xxx.yyy.zzz.configurer.FulizheLoginModule required debug=true;
};
- 在项目根目录创建名为
fulizhe.policy
的文件,内容如下:
grant {
permission javax.security.auth.AuthPermission "createLoginContext.demo";
permission javax.security.auth.AuthPermission "modifyPrincipals";
};
- 创建相关的Java类。
package com.xxx.yyy.zzz.configurer
public static class FulizheLoginModule implements LoginModule {
private Subject subject;
private CallbackHandler callbackHandler;
private boolean success = false;
private String user;
private String pasword;
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState,
Map<String, ?> options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
}
@Override
public boolean login() throws LoginException {
NameCallback nameCallback = new NameCallback("请输入用户名");
PasswordCallback passwordCallback = new PasswordCallback("请输入密码", false);
Callback[] callbacks = new Callback[] { nameCallback, passwordCallback };
try {
//执行回调,回调过程中获取用户名与密码
callbackHandler.handle(callbacks);
//得到用户名与密码
user = nameCallback.getName();
pasword = new String(passwordCallback.getPassword());
} catch (IOException | UnsupportedCallbackException e) {
success = false;
throw new FailedLoginException("用户名或密码获取失败");
}
//为简单起见认证条件写死
if ("fulizhe".equals(user) && "fulizhe".equals(pasword)) {
success = true;//认证成功
}
return true;
}
@Override
public boolean commit() throws LoginException {
if (!success) {
return false;
} else {
//如果认证成功则得subject中添加一个Principal对象
//这样某身份用户就认证通过并登录了该应用,即表明了谁在执行该程序
this.subject.getPrincipals().add(new KanqPrincipal(user));
return true;
}
}
@Override
public boolean abort() throws LoginException {
logout();
return true;
}
@Override
public boolean logout() throws LoginException {
//退出时将相应的Principal对象从subject中移除
Iterator<Principal> iter = subject.getPrincipals().iterator();
while (iter.hasNext()) {
Principal principal = iter.next();
if (principal instanceof KanqPrincipal && principal.getName().equals(user)) {
iter.remove();
break;
}
}
return true;
}
}
public static class KanqPrincipal implements Principal {
private String name;
public KanqPrincipal(String name) {
this.name = name;
}
@Override
public String getName() {
return this.name;
}
}
- 在系统初始化完毕的事件回调位置注册系统属性
System.setProperty("java.security.auth.login.config", "fulizhe.config");
System.setProperty("java.security.policy", "fulizhe.policy");
- 启动应用,测试效果。
提示输入凭证
鉴权通过(用户名和密码都是 fulizhe)