来源:http://www.xeclipse.com/?p=1359
在使用Spring Security的时候,遇到一个比较特殊的情况,需要根据用户名、邮箱等多个条件去验证用户或者使用第三方的验证服务来进行用户名和密码的判断,这样SS(Spring Security,一下简称SS)内置的authentication provider和user detail service就不能用了,花了一些时间去寻找其他的办法。
在尝试了修改Filter、替换SS内置的Filter之后,发现了一个比较简单的方法,这里简单的讲讲思路。
先看看SS验证用户名和密码的过程:
DelegatingFilterProxy(Security filter chain):
这些都是内置的Filter,请求会从上往下依次过滤。这里由于我们主要关心用户名和密码的验证,所以就要从UsernamePasswordAuthenticationFilter下手了。(UsernamePasswordAuthenticationFilter需要AuthenticationManager去进行验证。)
在SS的配置文件里面可以看到,如何给SS传递用户验证信息数据源(设置AuthenticationManager):
1
2
3
4
5
6
7
|
<
authentication-manager
>
<
authentication-provider
>
<
user-service
>
<
user
name
=
"admin"
authorities
=
"ROLE_USER"
password
=
"admin"
/>
user-service
>
authentication-provider
>
authentication-manager
>
|
当然这里是一个最简单的配置,跟踪一下代码就会发现:
1
|
UserDetails loadUserByUsername(String username)
throws
UsernameNotFoundException;
|
1
2
3
4
5
6
7
8
9
|
<
authentication-manager
alias
=
"authenticationManager"
>
<
authentication-provider
ref
=
"loginAuthenticationProvider"
>
authentication-provider
>
authentication-manager
>
<
bean
id
=
"loginAuthenticationProvider"
class
=
"com.XXX.examples.security.LoginAuthenticationProvider"
>
<
property
name
=
"userDetailsService"
ref
=
"loginUserDetailService"
>
property
>
bean
>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
|
public
class
LoginAuthenticationProvider
extends
AbstractUserDetailsAuthenticationProvider
{
// ~ Instance fields
// ================================================================================================
private
PasswordEncoder passwordEncoder =
new
PlaintextPasswordEncoder();
private
SaltSource saltSource;
private
LoginUserDetailsService userDetailsService;
// ~ Methods
// ========================================================================================================
protected
void
additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws
AuthenticationException
{
Object salt =
null
;
if
(
this
.saltSource !=
null
)
{
salt =
this
.saltSource.getSalt(userDetails);
}
if
(authentication.getCredentials() ==
null
)
{
logger.debug(
"Authentication failed: no credentials provided"
);
throw
new
BadCredentialsException(
"Bad credentials:"
+ userDetails);
}
String presentedPassword = authentication.getCredentials().toString();
if
(!passwordEncoder.isPasswordValid(userDetails.getPassword(), presentedPassword, salt))
{
logger.debug(
"Authentication failed: password does not match stored value"
);
throw
new
BadCredentialsException(
"Bad credentials:"
+ userDetails);
}
}
protected
void
doAfterPropertiesSet()
throws
Exception
{
Assert.notNull(
this
.userDetailsService,
"A UserDetailsService must be set"
);
}
protected
PasswordEncoder getPasswordEncoder()
{
return
passwordEncoder;
}
protected
SaltSource getSaltSource()
{
return
saltSource;
}
protected
LoginUserDetailsService getUserDetailsService()
{
return
userDetailsService;
}
protected
final
UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws
AuthenticationException
{
UserDetails loadedUser;
try
{
String password = (String) authentication.getCredentials();
loadedUser = getUserDetailsService().loadUserByUsername(username, password);
//区别在这里
}
catch
(UsernameNotFoundException notFound)
{
throw
notFound;
}
catch
(Exception repositoryProblem)
{
throw
new
AuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);
}
if
(loadedUser ==
null
)
{
throw
new
AuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation"
);
}
return
loadedUser;
}
/**
* Sets the PasswordEncoder instance to be used to encode and validate
* passwords. If not set, the password will be compared as plain text.
*
* For systems which are already using salted password which are encoded
* with a previous release, the encoder should be of type
* {@code org.springframework.security.authentication.encoding.PasswordEncoder}
* . Otherwise, the recommended approach is to use
* {@code org.springframework.security.crypto.password.PasswordEncoder}.
*
* @param passwordEncoder
* must be an instance of one of the {@code PasswordEncoder}
* types.
*/
public
void
setPasswordEncoder(Object passwordEncoder)
{
Assert.notNull(passwordEncoder,
"passwordEncoder cannot be null"
);
if
(passwordEncoder
instanceof
PasswordEncoder)
{
this
.passwordEncoder = (PasswordEncoder) passwordEncoder;
return
;
}
if
(passwordEncoder
instanceof
org.springframework.security.crypto.password.PasswordEncoder)
{
final
org.springframework.security.crypto.password.PasswordEncoder delegate = (org.springframework.security.crypto.password.PasswordEncoder) passwordEncoder;
this
.passwordEncoder =
new
PasswordEncoder()
{
private
void
checkSalt(Object salt)
{
Assert.isNull(salt,
"Salt value must be null when used with crypto module PasswordEncoder"
);
}
public
String encodePassword(String rawPass, Object salt)
{
checkSalt(salt);
return
delegate.encode(rawPass);
}
public
boolean
isPasswordValid(String encPass, String rawPass, Object salt)
{
checkSalt(salt);
return
delegate.matches(rawPass, encPass);
}
};
return
;
}
throw
new
IllegalArgumentException(
"passwordEncoder must be a PasswordEncoder instance"
);
}
/**
* The source of salts to use when decoding passwords.
* a valid value, meaning the
* present
*
* Instead, it is recommended that you use an encoder which uses a random
* salt and combines it with the password field. This is the default
* approach taken in the
* {@code org.springframework.security.crypto.password} package.
*
* @param saltSource
* to use when attempting to decode passwords via the
*
*/
public
void
setSaltSource(SaltSource saltSource)
{
this
.saltSource = saltSource;
}
public
void
setUserDetailsService(LoginUserDetailsService userDetailsService)
{
this
.userDetailsService = userDetailsService;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public
interface
LoginUserDetailsService
{
/**
* 功能描述:根据用户米密码验证用户信息
*
* 前置条件:
*
* 方法影响:
*
* Author , 2012-9-26
*
* @since server 2.0
* @param username
* @param password
* @return
* @throws UsernameNotFoundException
*/
UserDetails loadUserByUsername(String username, String password)
throws
UsernameNotFoundException;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
public
class
LoginUserDetailsServiceImpl
implements
LoginUserDetailsService
{
private
UserAccountService userAccountService;
/**
*
*/
public
LoginUserDetailsServiceImpl()
{
}
/**
* getter method
*
* @see LoginUserDetailsServiceImpl#userAccountService
* @return the userAccountService
*/
public
UserAccountService getUserAccountService()
{
return
userAccountService;
}
/**
* 功能描述:查找登录的用户
*
* 前置条件:
*
* 方法影响:
*
* Author , 2012-9-26
*
* @since server 2.0
* @param username
* @return
*/
public
UserDetails loadUserByUsername(String username, String password)
throws
UsernameNotFoundException
{
boolean
result = userAccountService.validate(username, password);
if
(!result)
{
return
null
;
}
List authorities =
new
ArrayList();
GrantedAuthorityImpl grantedAuthorityImpl =
new
GrantedAuthorityImpl();
authorities.add(grantedAuthorityImpl);
LoginUserDetailsImpl user =
new
LoginUserDetailsImpl(username, password, authorities);
grantedAuthorityImpl.setDelegate(user);
}
/**
* setter method
*
* @see LoginUserDetailsServiceImpl#userAccountService
* @param userAccountService
* the userAccountService to set
*/
public
void
setUserAccountService(UserAccountService userAccountService)
{
this
.userAccountService = userAccountService;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
public
class
GrantedAuthorityImpl
implements
GrantedAuthority
{
/**
* ROLE USER 权限
*/
private
static
final
String ROLE_USER =
"ROLE_USER"
;
/**
* Serial version UID
*/
private
static
final
long
serialVersionUID = 1L;
private
UserDetails delegate;
public
GrantedAuthorityImpl(UserDetails user)
{
this
.delegate = user;
}
/**
*
*/
public
GrantedAuthorityImpl()
{
}
public
String getAuthority()
{
return
ROLE_USER;
}
/**
* setter method
*
* @see GrantedAuthorityImpl#delegate
* @param delegate
* the delegate to set
*/
public
void
setDelegate(UserDetails delegate)
{
this
.delegate = delegate;
}
/**
* getter method
*
* @see GrantedAuthorityImpl#delegate
* @return the delegate
*/
public
UserDetails getDelegate()
{
return
delegate;
}
}
|
1
2
|