1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.shiro.realm.jdbc;
20
21 import org.apache.shiro.authc.AccountException;
22 import org.apache.shiro.authc.AuthenticationException;
23 import org.apache.shiro.authc.AuthenticationInfo;
24 import org.apache.shiro.authc.AuthenticationToken;
25 import org.apache.shiro.authc.SimpleAuthenticationInfo;
26 import org.apache.shiro.authc.UnknownAccountException;
27 import org.apache.shiro.authc.UsernamePasswordToken;
28 import org.apache.shiro.authz.AuthorizationException;
29 import org.apache.shiro.authz.AuthorizationInfo;
30 import org.apache.shiro.authz.SimpleAuthorizationInfo;
31 import org.apache.shiro.codec.Base64;
32 import org.apache.shiro.config.ConfigurationException;
33 import org.apache.shiro.realm.AuthorizingRealm;
34 import org.apache.shiro.subject.PrincipalCollection;
35 import org.apache.shiro.util.ByteSource;
36 import org.apache.shiro.util.JdbcUtils;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 import javax.sql.DataSource;
41 import java.sql.Connection;
42 import java.sql.PreparedStatement;
43 import java.sql.ResultSet;
44 import java.sql.SQLException;
45 import java.util.Collection;
46 import java.util.LinkedHashSet;
47 import java.util.Set;
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 public class JdbcRealm extends AuthorizingRealm {
65
66
67
68
69
70
71
72
73
74 protected static final String DEFAULT_AUTHENTICATION_QUERY = "select password from users where username = ?";
75
76
77
78
79 protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY = "select password, password_salt from users where username = ?";
80
81
82
83
84 protected static final String DEFAULT_USER_ROLES_QUERY = "select role_name from user_roles where username = ?";
85
86
87
88
89 protected static final String DEFAULT_PERMISSIONS_QUERY = "select permission from roles_permissions where role_name = ?";
90
91 private static final Logger log = LoggerFactory.getLogger(JdbcRealm.class);
92
93
94
95
96
97
98
99
100
101 public enum SaltStyle {NO_SALT, CRYPT, COLUMN, EXTERNAL};
102
103
104
105
106 protected DataSource dataSource;
107
108 protected String authenticationQuery = DEFAULT_AUTHENTICATION_QUERY;
109
110 protected String userRolesQuery = DEFAULT_USER_ROLES_QUERY;
111
112 protected String permissionsQuery = DEFAULT_PERMISSIONS_QUERY;
113
114 protected boolean permissionsLookupEnabled = false;
115
116 protected SaltStyle saltStyle = SaltStyle.NO_SALT;
117
118 protected boolean saltIsBase64Encoded = true;
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133 public void setDataSource(DataSource dataSource) {
134 this.dataSource = dataSource;
135 }
136
137
138
139
140
141
142
143
144
145
146
147 public void setAuthenticationQuery(String authenticationQuery) {
148 this.authenticationQuery = authenticationQuery;
149 }
150
151
152
153
154
155
156
157
158
159
160
161 public void setUserRolesQuery(String userRolesQuery) {
162 this.userRolesQuery = userRolesQuery;
163 }
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180 public void setPermissionsQuery(String permissionsQuery) {
181 this.permissionsQuery = permissionsQuery;
182 }
183
184
185
186
187
188
189
190
191 public void setPermissionsLookupEnabled(boolean permissionsLookupEnabled) {
192 this.permissionsLookupEnabled = permissionsLookupEnabled;
193 }
194
195
196
197
198
199
200 public void setSaltStyle(SaltStyle saltStyle) {
201 this.saltStyle = saltStyle;
202 if (saltStyle == SaltStyle.COLUMN && authenticationQuery.equals(DEFAULT_AUTHENTICATION_QUERY)) {
203 authenticationQuery = DEFAULT_SALTED_AUTHENTICATION_QUERY;
204 }
205 }
206
207
208
209
210
211
212
213
214 public void setSaltIsBase64Encoded(boolean saltIsBase64Encoded) {
215 this.saltIsBase64Encoded = saltIsBase64Encoded;
216 }
217
218
219
220
221
222 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
223
224 UsernamePasswordTokenrg/apache/shiro/authc/UsernamePasswordToken.html#UsernamePasswordToken">UsernamePasswordToken upToken = (UsernamePasswordToken) token;
225 String username = upToken.getUsername();
226
227
228 if (username == null) {
229 throw new AccountException("Null usernames are not allowed by this realm.");
230 }
231
232 Connection conn = null;
233 SimpleAuthenticationInfo info = null;
234 try {
235 conn = dataSource.getConnection();
236
237 String password = null;
238 String salt = null;
239 switch (saltStyle) {
240 case NO_SALT:
241 password = getPasswordForUser(conn, username)[0];
242 break;
243 case CRYPT:
244
245 throw new ConfigurationException("Not implemented yet");
246
247 case COLUMN:
248 String[] queryResults = getPasswordForUser(conn, username);
249 password = queryResults[0];
250 salt = queryResults[1];
251 break;
252 case EXTERNAL:
253 password = getPasswordForUser(conn, username)[0];
254 salt = getSaltForUser(username);
255 }
256
257 if (password == null) {
258 throw new UnknownAccountException("No account found for user [" + username + "]");
259 }
260
261 info = new SimpleAuthenticationInfo(username, password.toCharArray(), getName());
262
263 if (salt != null) {
264 if (saltStyle == SaltStyle.COLUMN && saltIsBase64Encoded) {
265 info.setCredentialsSalt(ByteSource.Util.bytes(Base64.decode(salt)));
266 } else {
267 info.setCredentialsSalt(ByteSource.Util.bytes(salt));
268 }
269 }
270
271 } catch (SQLException e) {
272 final String message = "There was a SQL error while authenticating user [" + username + "]";
273 if (log.isErrorEnabled()) {
274 log.error(message, e);
275 }
276
277
278 throw new AuthenticationException(message, e);
279 } finally {
280 JdbcUtils.closeConnection(conn);
281 }
282
283 return info;
284 }
285
286 private String[] getPasswordForUser(Connection conn, String username) throws SQLException {
287
288 String[] result;
289 boolean returningSeparatedSalt = false;
290 switch (saltStyle) {
291 case NO_SALT:
292 case CRYPT:
293 case EXTERNAL:
294 result = new String[1];
295 break;
296 default:
297 result = new String[2];
298 returningSeparatedSalt = true;
299 }
300
301 PreparedStatement ps = null;
302 ResultSet rs = null;
303 try {
304 ps = conn.prepareStatement(authenticationQuery);
305 ps.setString(1, username);
306
307
308 rs = ps.executeQuery();
309
310
311 boolean foundResult = false;
312 while (rs.next()) {
313
314
315 if (foundResult) {
316 throw new AuthenticationException("More than one user row found for user [" + username + "]. Usernames must be unique.");
317 }
318
319 result[0] = rs.getString(1);
320 if (returningSeparatedSalt) {
321 result[1] = rs.getString(2);
322 }
323
324 foundResult = true;
325 }
326 } finally {
327 JdbcUtils.closeResultSet(rs);
328 JdbcUtils.closeStatement(ps);
329 }
330
331 return result;
332 }
333
334
335
336
337
338
339
340 @Override
341 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
342
343
344 if (principals == null) {
345 throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
346 }
347
348 String username = (String) getAvailablePrincipal(principals);
349
350 Connection conn = null;
351 Set<String> roleNames = null;
352 Set<String> permissions = null;
353 try {
354 conn = dataSource.getConnection();
355
356
357 roleNames = getRoleNamesForUser(conn, username);
358 if (permissionsLookupEnabled) {
359 permissions = getPermissions(conn, username, roleNames);
360 }
361
362 } catch (SQLException e) {
363 final String message = "There was a SQL error while authorizing user [" + username + "]";
364 if (log.isErrorEnabled()) {
365 log.error(message, e);
366 }
367
368
369 throw new AuthorizationException(message, e);
370 } finally {
371 JdbcUtils.closeConnection(conn);
372 }
373
374 SimpleAuthorizationInfoonInfo.html#SimpleAuthorizationInfo">SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);
375 info.setStringPermissions(permissions);
376 return info;
377
378 }
379
380 protected Set<String> getRoleNamesForUser(Connection conn, String username) throws SQLException {
381 PreparedStatement ps = null;
382 ResultSet rs = null;
383 Set<String> roleNames = new LinkedHashSet<String>();
384 try {
385 ps = conn.prepareStatement(userRolesQuery);
386 ps.setString(1, username);
387
388
389 rs = ps.executeQuery();
390
391
392 while (rs.next()) {
393
394 String roleName = rs.getString(1);
395
396
397 if (roleName != null) {
398 roleNames.add(roleName);
399 } else {
400 if (log.isWarnEnabled()) {
401 log.warn("Null role name found while retrieving role names for user [" + username + "]");
402 }
403 }
404 }
405 } finally {
406 JdbcUtils.closeResultSet(rs);
407 JdbcUtils.closeStatement(ps);
408 }
409 return roleNames;
410 }
411
412 protected Set<String> getPermissions(Connection conn, String username, Collection<String> roleNames) throws SQLException {
413 PreparedStatement ps = null;
414 Set<String> permissions = new LinkedHashSet<String>();
415 try {
416 ps = conn.prepareStatement(permissionsQuery);
417 for (String roleName : roleNames) {
418
419 ps.setString(1, roleName);
420
421 ResultSet rs = null;
422
423 try {
424
425 rs = ps.executeQuery();
426
427
428 while (rs.next()) {
429
430 String permissionString = rs.getString(1);
431
432
433 permissions.add(permissionString);
434 }
435 } finally {
436 JdbcUtils.closeResultSet(rs);
437 }
438
439 }
440 } finally {
441 JdbcUtils.closeStatement(ps);
442 }
443
444 return permissions;
445 }
446
447 protected String getSaltForUser(String username) {
448 return username;
449 }
450
451 }