@Scope(APPLICATION)
@Name("org.jboss.seam.web.authenticationFilter")
@Install(value = false, precedence = BUILT_IN)
@BypassInterceptors
@Filter(within = "org.jboss.seam.web.exceptionFilter")
public class AuthenticationFilter extends AbstractFilter
{
private static final String DEFAULT_REALM = "seamApp";
private static final String AUTH_TYPE_BASIC = "basic";
private static final String AUTH_TYPE_DIGEST = "digest";
@Logger Log log;
public enum AuthType {basic, digest}
private String realm = DEFAULT_REALM;
private String key;
private int nonceValiditySeconds = 300;
private String authType = AUTH_TYPE_BASIC;
public void setRealm(String realm)
{
this.realm = realm;
}
public String getRealm()
{
return realm;
}
public void setAuthType(String authType)
{
this.authType = authType;
}
public String getAuthType()
{
return authType;
}
public String getKey()
{
return key;
}
public void setKey(String key)
{
this.key = key;
}
public int getNonceValiditySeconds()
{
return nonceValiditySeconds;
}
public void setNonceValiditySeconds(int value)
{
this.nonceValiditySeconds = value;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
if (!(request instanceof HttpServletRequest))
{
throw new ServletException("This filter can only process HttpServletRequest requests");
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// Force session creation
httpRequest.getSession();
if (AUTH_TYPE_BASIC.equals(authType))
processBasicAuth(httpRequest, httpResponse, chain);
else if (AUTH_TYPE_DIGEST.equals(authType))
processDigestAuth(httpRequest, httpResponse, chain);
else
throw new ServletException("Invalid authentication type");
}
private void processBasicAuth(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
{
Context ctx = new SessionContext( new ServletRequestSessionMap(request) );
Identity identity = (Identity) ctx.get(Identity.class);
if (identity == null)
{
throw new ServletException("Identity not found - please ensure that the Identity component is created on startup.");
}
Credentials credentials = (Credentials) ctx.get(Credentials.class);
boolean requireAuth = false;
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Basic "))
{
String base64Token = header.substring(6);
String token = new String(Base64.decode(base64Token));
String username = "";
String password = "";
int delim = token.indexOf(":");
if (delim != -1)
{
username = token.substring(0, delim);
password = token.substring(delim + 1);
}
// Only reauthenticate if username doesn't match Identity.username and user isn't authenticated
if (!username.equals(credentials.getUsername()) || !identity.isLoggedIn())
{
try
{
credentials.setPassword(password);
authenticate( request, username );
}
catch (Exception ex)
{
log.warn("Error authenticating: " + ex.getMessage());
requireAuth = true;
}
}
}
if (!identity.isLoggedIn() && !credentials.isSet())
{
requireAuth = true;
}
try
{
if (!requireAuth)
{
chain.doFilter(request, response);
return;
}
}
catch (NotLoggedInException ex)
{
requireAuth = true;
}
if ((requireAuth && !identity.isLoggedIn()))
{
response.addHeader("WWW-Authenticate", "Basic realm=\"" + realm + "\"");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Not authorized");
}
}
private void processDigestAuth(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
{
Context ctx = new SessionContext( new ServletRequestSessionMap(request) );
Identity identity = (Identity) ctx.get(Identity.class);
if (identity == null)
{
throw new ServletException("Identity not found - please ensure that the Identity component is created on startup.");
}
Credentials credentials = (Credentials) ctx.get(Credentials.class);
boolean requireAuth = false;
boolean nonceExpired = false;
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Digest "))
{
String section212response = header.substring(7);
String[] headerEntries = section212response.split(",");
Map<String,String> headerMap = new HashMap<String,String>();
for (String entry : headerEntries)
{
String[] vals = split(entry, "=");
headerMap.put(vals[0].trim(), vals[1].replace("\"", "").trim());
}
DigestRequest digestRequest = new DigestRequest();
digestRequest.setHttpMethod(request.getMethod());
digestRequest.setSystemRealm(realm);
digestRequest.setRealm(headerMap.get("realm"));
digestRequest.setKey(key);
digestRequest.setNonce(headerMap.get("nonce"));
digestRequest.setUri(headerMap.get("uri"));
digestRequest.setClientDigest(headerMap.get("response"));
digestRequest.setQop(headerMap.get("qop"));
digestRequest.setNonceCount(headerMap.get("nc"));
digestRequest.setClientNonce(headerMap.get("cnonce"));
try
{
digestRequest.validate();
request.getSession().setAttribute(DigestRequest.DIGEST_REQUEST, digestRequest);
authenticate( request, headerMap.get("username") );
}
catch (DigestValidationException ex)
{
log.warn(String.format("Digest validation failed, header [%s]: %s",
section212response, ex.getMessage()));
requireAuth = true;
if (ex.isNonceExpired()) nonceExpired = true;
}
catch (Exception ex)
{
log.warn("Error authenticating: " + ex.getMessage());
requireAuth = true;
}
}
if (!identity.isLoggedIn() && !credentials.isSet())
{
requireAuth = true;
}
try
{
if (!requireAuth)
{
chain.doFilter(request, response);
return;
}
}
catch (NotLoggedInException ex)
{
requireAuth = true;
}
if ((requireAuth && !identity.isLoggedIn()))
{
long expiryTime = System.currentTimeMillis() + (nonceValiditySeconds * 1000);
String signatureValue = DigestUtils.md5Hex(expiryTime + ":" + key);
String nonceValue = expiryTime + ":" + signatureValue;
String nonceValueBase64 = Base64.encodeBytes(nonceValue.getBytes());
// qop is quality of protection, as defined by RFC 2617.
// we do not use opaque due to IE violation of RFC 2617 in not
// representing opaque on subsequent requests in same session.
String authenticateHeader = "Digest realm=\"" + realm + "\", " + "qop=\"auth\", nonce=\""
+ nonceValueBase64 + "\"";
if (nonceExpired) authenticateHeader = authenticateHeader + ", stale=\"true\"";
response.addHeader("WWW-Authenticate", authenticateHeader);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
private void authenticate(HttpServletRequest request, final String username)
throws ServletException, IOException
{
new ContextualHttpServletRequest(request)
{
@Override
public void process() throws ServletException, IOException, LoginException
{
Identity identity = Identity.instance();
identity.getCredentials().setUsername(username);
identity.authenticate();
}
}.run();
}
private String[] split(String toSplit, String delimiter)
{
if (delimiter.length() != 1)
{
throw new IllegalArgumentException("Delimiter can only be one character in length");
}
int offset = toSplit.indexOf(delimiter);
if (offset < 0) {
return null;
}
String beforeDelimiter = toSplit.substring(0, offset);
String afterDelimiter = toSplit.substring(offset + 1);
return new String[] {beforeDelimiter, afterDelimiter};
}
}
注:有部分代码是用来适配Acegi框架的.