Да, есть обходное решение (я сделал это для Tomcat 7.0.68).
1) Настройте ваш web.xml
использовать FORM аутентификации-метод:
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/loginError.jsp</form-error-page>
</form-login-config>
</login-config>
2) Установите url-pattern
вы хотите проверить подлинность BASIC путь без каких-либо auth-constraint
:
<security-constraint>
<web-resource-collection>
<web-resource-name>BASIC auth path</web-resource-name>
<url-pattern>/app/services/*</url-pattern>
</web-resource-collection>
</security-constraint>
3) Настройка фильтра для этой модели:
<filter>
<filter-name>BasicLoginFilter</filter-name>
<filter-class>pa.cka.ge.BasicLoginFilter</filter-class>
<init-param>
<param-name>role-names-comma-sep</param-name>
<param-value>role1,andRole2,andRole3</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>BasicLoginFilter</filter-name>
<url-pattern>/app/services/*</url-pattern>
</filter-mapping>
enter code here
, где role-names-comma-sep
- это ваш настраиваемый параметр, определяющий роли для доступа к /app/services
. Это полезно, потому что путь /app/services
не должен иметь ограничений auth (см. Выше), и вы, как правило, не можете определять роли как обычно. В моем примере реализация проверяет эти роли с помощью AND (вы можете ее изменить).
4) Войти Процесс вручную в фильтре:
package pa.cka.ge;
import java.io.IOException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Role;
import org.apache.catalina.users.MemoryUser;
import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.codec.binary.Base64;
public class BasicLoginFilter implements Filter {
/**
* List of roles the user must have to authenticate
*/
private final List<String> roleNames = new ArrayList<String>();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
String roleNamesParam = filterConfig.getInitParameter("role-names-comma-sep");
if (roleNamesParam != null) {
for (String roleName: roleNamesParam.split(",")) {
roleNames.add(roleName);
}
}
}
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String BASIC_PREFIX = "Basic ";
@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)resp;
// get username and password from the Authorization header
String authHeader = request.getHeader(AUTHORIZATION_HEADER);
if (authHeader == null || !authHeader.startsWith(BASIC_PREFIX)) {
throwBasicAuthRequired();
}
String userPassBase64 = authHeader.substring(BASIC_PREFIX.length());
String userPassDecoded = new String(Base64.decodeBase64(userPassBase64), B2CConverter.ISO_8859_1);// decode from base64 any other way, if this won't work for you. Finally userPassDecoded must contain readable "username:password"
if (!userPassDecoded.contains(":")) {
throwBasicAuthRequired();
}
String authUser = userPassDecoded.substring(0, userPassDecoded.indexOf(':'));
String authPass = userPassDecoded.substring(userPassDecoded.indexOf(':') + 1);
// do login manually
request.login(authUser, authPass);
// check roles for the user
final Principal userPrincipal = request.getUserPrincipal();
// Your Principal will be another class, not MemoryUser. Run in debug mode to see what class you actually have. The role checking will depend on that class.
MemoryUser user = (MemoryUser)userPrincipal;
boolean hasRoles = true;
for (String role: roleNames) {
if (role == null) {
continue;
}
boolean hasRole = false;
Iterator<Role> roles = user.getRoles();
while (roles.hasNext()) {
if (role.equals(roles.next().getName())) {
hasRole = true;
break;
}
}
if (!hasRole) {
hasRoles = false;
break;
}
}
if (hasRoles) {
// login successful
chain.doFilter(request, response);
request.logout();// optional
} else {
// login failed
throwLoginFailed();
}
}
@Override
public void destroy() {
}
public static void throwBasicAuthRequired() throws ServletException {
throw new ServletException("The /app/services resources require BASIC authentication");
}
public static void throwLoginFailed() throws ServletException {
throw new ServletException("Login failed");
}
}
Готово! Теперь /app/services
поддерживает BASIC auth, но остальное приложение поддерживает FORM.
Это невозможно в одном «войне», но вы можете создать два веб-модуля в одном «ухе» и настроить их по-разному и разделить бизнес-логику. Любая конкретная причина, почему вам нужны оба механизма в одном приложении? – Gas
Да, причина в том, что мне нужны услуги по уходу, разрешенные BASIC, и веб-приложение, разрешенное FORM. Спасибо за ответ. –
Тогда я бы предложил создать отдельный веб-модуль для службы отдыха и обмениваться общей бизнес-логикой через библиотеку или модуль ejb, это было бы самым простым и обеспечило бы хорошее разделение обязанностей. Также можно сделать 'post' в' j_security_check' от вашего клиента для отдыха. И если это внутренние службы, они могут использовать cookie из браузера. – Gas