Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
FormAuthenticationFilter |
|
| 1.5789473684210527;1.579 |
1 | /* | |
2 | * Licensed to the Apache Software Foundation (ASF) under one | |
3 | * or more contributor license agreements. See the NOTICE file | |
4 | * distributed with this work for additional information | |
5 | * regarding copyright ownership. The ASF licenses this file | |
6 | * to you under the Apache License, Version 2.0 (the | |
7 | * "License"); you may not use this file except in compliance | |
8 | * with the License. You may obtain a copy of the License at | |
9 | * | |
10 | * http://www.apache.org/licenses/LICENSE-2.0 | |
11 | * | |
12 | * Unless required by applicable law or agreed to in writing, | |
13 | * software distributed under the License is distributed on an | |
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
15 | * KIND, either express or implied. See the License for the | |
16 | * specific language governing permissions and limitations | |
17 | * under the License. | |
18 | */ | |
19 | package org.apache.shiro.web.filter.authc; | |
20 | ||
21 | import org.apache.shiro.authc.AuthenticationException; | |
22 | import org.apache.shiro.authc.AuthenticationToken; | |
23 | import org.apache.shiro.authc.UsernamePasswordToken; | |
24 | import org.apache.shiro.subject.Subject; | |
25 | import org.apache.shiro.web.util.WebUtils; | |
26 | import org.slf4j.Logger; | |
27 | import org.slf4j.LoggerFactory; | |
28 | ||
29 | import javax.servlet.ServletRequest; | |
30 | import javax.servlet.ServletResponse; | |
31 | import javax.servlet.http.HttpServletRequest; | |
32 | ||
33 | ||
34 | /** | |
35 | * Requires the requesting user to be authenticated for the request to continue, and if they are not, forces the user | |
36 | * to login via by redirecting them to the {@link #setLoginUrl(String) loginUrl} you configure. | |
37 | * <p/> | |
38 | * <p>This filter constructs a {@link UsernamePasswordToken UsernamePasswordToken} with the values found in | |
39 | * {@link #setUsernameParam(String) username}, {@link #setPasswordParam(String) password}, | |
40 | * and {@link #setRememberMeParam(String) rememberMe} request parameters. It then calls | |
41 | * {@link org.apache.shiro.subject.Subject#login(org.apache.shiro.authc.AuthenticationToken) Subject.login(usernamePasswordToken)}, | |
42 | * effectively automatically performing a login attempt. Note that the login attempt will only occur when the | |
43 | * {@link #isLoginSubmission(javax.servlet.ServletRequest, javax.servlet.ServletResponse) isLoginSubmission(request,response)} | |
44 | * is <code>true</code>, which by default occurs when the request is for the {@link #setLoginUrl(String) loginUrl} and | |
45 | * is a POST request. | |
46 | * <p/> | |
47 | * <p>If the login attempt fails, the resulting <code>AuthenticationException</code> fully qualified class name will | |
48 | * be set as a request attribute under the {@link #setFailureKeyAttribute(String) failureKeyAttribute} key. This | |
49 | * FQCN can be used as an i18n key or lookup mechanism to explain to the user why their login attempt failed | |
50 | * (e.g. no account, incorrect password, etc). | |
51 | * <p/> | |
52 | * <p>If you would prefer to handle the authentication validation and login in your own code, consider using the | |
53 | * {@link PassThruAuthenticationFilter} instead, which allows requests to the | |
54 | * {@link #loginUrl} to pass through to your application's code directly. | |
55 | * | |
56 | * @see PassThruAuthenticationFilter | |
57 | * @since 0.9 | |
58 | */ | |
59 | public class FormAuthenticationFilter extends AuthenticatingFilter { | |
60 | ||
61 | //TODO - complete JavaDoc | |
62 | ||
63 | public static final String DEFAULT_ERROR_KEY_ATTRIBUTE_NAME = "shiroLoginFailure"; | |
64 | ||
65 | public static final String DEFAULT_USERNAME_PARAM = "username"; | |
66 | public static final String DEFAULT_PASSWORD_PARAM = "password"; | |
67 | public static final String DEFAULT_REMEMBER_ME_PARAM = "rememberMe"; | |
68 | ||
69 | 1 | private static final Logger log = LoggerFactory.getLogger(FormAuthenticationFilter.class); |
70 | ||
71 | 50 | private String usernameParam = DEFAULT_USERNAME_PARAM; |
72 | 50 | private String passwordParam = DEFAULT_PASSWORD_PARAM; |
73 | 50 | private String rememberMeParam = DEFAULT_REMEMBER_ME_PARAM; |
74 | ||
75 | 50 | private String failureKeyAttribute = DEFAULT_ERROR_KEY_ATTRIBUTE_NAME; |
76 | ||
77 | 50 | public FormAuthenticationFilter() { |
78 | 50 | setLoginUrl(DEFAULT_LOGIN_URL); |
79 | 50 | } |
80 | ||
81 | @Override | |
82 | public void setLoginUrl(String loginUrl) { | |
83 | 51 | String previous = getLoginUrl(); |
84 | 51 | if (previous != null) { |
85 | 51 | this.appliedPaths.remove(previous); |
86 | } | |
87 | 51 | super.setLoginUrl(loginUrl); |
88 | 51 | if (log.isTraceEnabled()) { |
89 | 51 | log.trace("Adding login url to applied paths."); |
90 | } | |
91 | 51 | this.appliedPaths.put(getLoginUrl(), null); |
92 | 51 | } |
93 | ||
94 | public String getUsernameParam() { | |
95 | 0 | return usernameParam; |
96 | } | |
97 | ||
98 | /** | |
99 | * Sets the request parameter name to look for when acquiring the username. Unless overridden by calling this | |
100 | * method, the default is <code>username</code>. | |
101 | * | |
102 | * @param usernameParam the name of the request param to check for acquiring the username. | |
103 | */ | |
104 | public void setUsernameParam(String usernameParam) { | |
105 | 0 | this.usernameParam = usernameParam; |
106 | 0 | } |
107 | ||
108 | public String getPasswordParam() { | |
109 | 0 | return passwordParam; |
110 | } | |
111 | ||
112 | /** | |
113 | * Sets the request parameter name to look for when acquiring the password. Unless overridden by calling this | |
114 | * method, the default is <code>password</code>. | |
115 | * | |
116 | * @param passwordParam the name of the request param to check for acquiring the password. | |
117 | */ | |
118 | public void setPasswordParam(String passwordParam) { | |
119 | 0 | this.passwordParam = passwordParam; |
120 | 0 | } |
121 | ||
122 | public String getRememberMeParam() { | |
123 | 0 | return rememberMeParam; |
124 | } | |
125 | ||
126 | /** | |
127 | * Sets the request parameter name to look for when acquiring the rememberMe boolean value. Unless overridden | |
128 | * by calling this method, the default is <code>rememberMe</code>. | |
129 | * <p/> | |
130 | * RememberMe will be <code>true</code> if the parameter value equals any of those supported by | |
131 | * {@link org.apache.shiro.web.util.WebUtils#isTrue(javax.servlet.ServletRequest, String) WebUtils.isTrue(request,value)}, <code>false</code> | |
132 | * otherwise. | |
133 | * | |
134 | * @param rememberMeParam the name of the request param to check for acquiring the rememberMe boolean value. | |
135 | */ | |
136 | public void setRememberMeParam(String rememberMeParam) { | |
137 | 0 | this.rememberMeParam = rememberMeParam; |
138 | 0 | } |
139 | ||
140 | public String getFailureKeyAttribute() { | |
141 | 0 | return failureKeyAttribute; |
142 | } | |
143 | ||
144 | public void setFailureKeyAttribute(String failureKeyAttribute) { | |
145 | 0 | this.failureKeyAttribute = failureKeyAttribute; |
146 | 0 | } |
147 | ||
148 | protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { | |
149 | 0 | if (isLoginRequest(request, response)) { |
150 | 0 | if (isLoginSubmission(request, response)) { |
151 | 0 | if (log.isTraceEnabled()) { |
152 | 0 | log.trace("Login submission detected. Attempting to execute login."); |
153 | } | |
154 | 0 | return executeLogin(request, response); |
155 | } else { | |
156 | 0 | if (log.isTraceEnabled()) { |
157 | 0 | log.trace("Login page view."); |
158 | } | |
159 | //allow them to see the login page ;) | |
160 | 0 | return true; |
161 | } | |
162 | } else { | |
163 | 0 | if (log.isTraceEnabled()) { |
164 | 0 | log.trace("Attempting to access a path which requires authentication. Forwarding to the " + |
165 | "Authentication url [" + getLoginUrl() + "]"); | |
166 | } | |
167 | ||
168 | 0 | saveRequestAndRedirectToLogin(request, response); |
169 | 0 | return false; |
170 | } | |
171 | } | |
172 | ||
173 | /** | |
174 | * This default implementation merely returns <code>true</code> if the request is an HTTP <code>POST</code>, | |
175 | * <code>false</code> otherwise. Can be overridden by subclasses for custom login submission detection behavior. | |
176 | * | |
177 | * @param request the incoming ServletRequest | |
178 | * @param response the outgoing ServletResponse. | |
179 | * @return <code>true</code> if the request is an HTTP <code>POST</code>, <code>false</code> otherwise. | |
180 | */ | |
181 | @SuppressWarnings({"UnusedDeclaration"}) | |
182 | protected boolean isLoginSubmission(ServletRequest request, ServletResponse response) { | |
183 | 0 | return (request instanceof HttpServletRequest) && WebUtils.toHttp(request).getMethod().equalsIgnoreCase(POST_METHOD); |
184 | } | |
185 | ||
186 | protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) { | |
187 | 0 | String username = getUsername(request); |
188 | 0 | String password = getPassword(request); |
189 | 0 | return createToken(username, password, request, response); |
190 | } | |
191 | ||
192 | protected boolean isRememberMe(ServletRequest request) { | |
193 | 0 | return WebUtils.isTrue(request, getRememberMeParam()); |
194 | } | |
195 | ||
196 | protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, | |
197 | ServletRequest request, ServletResponse response) throws Exception { | |
198 | 0 | issueSuccessRedirect(request, response); |
199 | //we handled the success redirect directly, prevent the chain from continuing: | |
200 | 0 | return false; |
201 | } | |
202 | ||
203 | protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, | |
204 | ServletRequest request, ServletResponse response) { | |
205 | 0 | if (log.isDebugEnabled()) { |
206 | 0 | log.debug( "Authentication exception", e ); |
207 | } | |
208 | 0 | setFailureAttribute(request, e); |
209 | //login failed, let request continue back to the login page: | |
210 | 0 | return true; |
211 | } | |
212 | ||
213 | protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) { | |
214 | 0 | String className = ae.getClass().getName(); |
215 | 0 | request.setAttribute(getFailureKeyAttribute(), className); |
216 | 0 | } |
217 | ||
218 | protected String getUsername(ServletRequest request) { | |
219 | 0 | return WebUtils.getCleanParam(request, getUsernameParam()); |
220 | } | |
221 | ||
222 | protected String getPassword(ServletRequest request) { | |
223 | 0 | return WebUtils.getCleanParam(request, getPasswordParam()); |
224 | } | |
225 | ||
226 | ||
227 | } |