我需要根据Java EE应用程序中的Web资源配置BASIC和FORM身份验证方法。这意味着例如对于路径/ app / services我想使用BASIC进行身份验证,而其余的应用程序方法将是FORM。
没有Spring,甚至纯Java EE中是否可能?
答案 0 :(得分:2)
这是可能的,但您需要创建并安装自己的身份验证模块,而不是使用两个内置的BASIC和FORM。
Java EE有一个名为JASPIC的API / SPI。最重要的是,许多应用程序服务器都有替代的本机API。
答案 1 :(得分:1)
是的,有一种解决方法(我是为Tomcat 7.0.68做的)。
1)配置web.xml
以使用FORM auth-method:
<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)设置您要验证BASIC方式的url-pattern
,而不是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-constraints(参见上文),并且您基本上无法像往常一样定义角色。在我的示例中,实现通过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身份验证,但其余应用程序支持FORM。
答案 2 :(得分:0)
从Servlet 3.0(Tomcat 7+)开始,您可以以编程方式执行HttpServletRequest.login()
获取用户:传递罗马诺夫的答案并登录。
答案 3 :(得分:-1)
Spring安全性会安装自己的过滤器。您可以编写自己的过滤器/过滤器并安装它们。如果您可以轻松识别哪个URL模式使用一个身份验证,另一个使用两个过滤器则相当简单