0
# User Details
1
2
Specialized user details services for extracting user information and authorities from CAS assertions. These services integrate CAS user attributes with Spring Security's user details framework to populate user authorities and profile information.
3
4
## Capabilities
5
6
### Abstract CAS Assertion User Details Service
7
8
Base class for implementing user details services that load user information from CAS assertions rather than traditional username lookup.
9
10
```java { .api }
11
/**
12
* Base class for user details services that construct UserDetails from CAS assertions.
13
* Provides framework for loading user information from CAS assertion attributes.
14
*/
15
public abstract class AbstractCasAssertionUserDetailsService
16
implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {
17
18
/**
19
* Loads user details from CAS assertion authentication token.
20
* This method is final and delegates to the abstract loadUserDetails(Assertion) method.
21
* @param token authentication token containing CAS assertion
22
* @return UserDetails populated from CAS assertion
23
*/
24
public final UserDetails loadUserDetails(CasAssertionAuthenticationToken token);
25
26
/**
27
* Loads user details from CAS assertion.
28
* Implementations should extract user information and authorities from the assertion.
29
* @param assertion CAS assertion containing user attributes
30
* @return UserDetails object populated from assertion
31
*/
32
protected abstract UserDetails loadUserDetails(Assertion assertion);
33
}
34
```
35
36
### Granted Authority from Assertion Attributes User Details Service
37
38
Concrete implementation that creates user authorities from specific CAS assertion attributes.
39
40
```java { .api }
41
/**
42
* User details service that extracts granted authorities from specified CAS assertion attributes.
43
* Creates GrantedAuthority objects from assertion attribute values.
44
*/
45
public final class GrantedAuthorityFromAssertionAttributesUserDetailsService
46
extends AbstractCasAssertionUserDetailsService {
47
48
/**
49
* Creates service that extracts authorities from specified assertion attributes.
50
* @param attributes array of assertion attribute names to use for authorities
51
* @throws IllegalArgumentException if attributes array is null or empty
52
*/
53
public GrantedAuthorityFromAssertionAttributesUserDetailsService(String[] attributes);
54
55
/**
56
* Sets whether to convert attribute values to uppercase before creating authorities.
57
* Useful for normalizing role names (e.g., "admin" becomes "ADMIN").
58
* @param convertToUpperCase true to convert authorities to uppercase
59
*/
60
public void setConvertToUpperCase(boolean convertToUpperCase);
61
62
/**
63
* Loads user details from CAS assertion by extracting authorities from configured attributes.
64
* @param assertion CAS assertion containing user attributes
65
* @return UserDetails with authorities populated from assertion attributes
66
*/
67
protected UserDetails loadUserDetails(Assertion assertion);
68
}
69
```
70
71
**Usage Example:**
72
73
```java
74
@Bean
75
public GrantedAuthorityFromAssertionAttributesUserDetailsService casUserDetailsService() {
76
String[] attributes = {"role", "group", "department"};
77
GrantedAuthorityFromAssertionAttributesUserDetailsService service =
78
new GrantedAuthorityFromAssertionAttributesUserDetailsService(attributes);
79
service.setConvertToUpperCase(true);
80
return service;
81
}
82
```
83
84
## Custom User Details Service Implementation
85
86
Example of creating a custom user details service that extends the abstract base:
87
88
```java
89
public class CustomCasUserDetailsService extends AbstractCasAssertionUserDetailsService {
90
91
private UserRepository userRepository;
92
93
public CustomCasUserDetailsService(UserRepository userRepository) {
94
this.userRepository = userRepository;
95
}
96
97
@Override
98
protected UserDetails loadUserDetails(Assertion assertion) {
99
String username = assertion.getPrincipal().getName();
100
Map<String, Object> attributes = assertion.getPrincipal().getAttributes();
101
102
// Load user from database
103
User user = userRepository.findByUsername(username);
104
if (user == null) {
105
// Handle user not found - could return a default user or throw a runtime exception
106
throw new IllegalStateException("User not found: " + username);
107
}
108
109
// Extract roles from CAS attributes
110
List<String> roles = (List<String>) attributes.get("roles");
111
List<GrantedAuthority> authorities = roles.stream()
112
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.toUpperCase()))
113
.collect(Collectors.toList());
114
115
// Create UserDetails with additional attributes
116
return User.builder()
117
.username(username)
118
.password("") // Password not needed for CAS
119
.authorities(authorities)
120
.accountNonExpired(true)
121
.accountNonLocked(true)
122
.credentialsNonExpired(true)
123
.disabled(false)
124
.build();
125
}
126
}
127
```
128
129
## Configuration Examples
130
131
### Basic Attribute-Based Authorities
132
133
```java
134
@Configuration
135
public class CasUserDetailsConfig {
136
137
@Bean
138
public AuthenticationUserDetailsService<CasAssertionAuthenticationToken> casUserDetailsService() {
139
// Extract authorities from 'role' attribute
140
String[] attributes = {"role"};
141
GrantedAuthorityFromAssertionAttributesUserDetailsService service =
142
new GrantedAuthorityFromAssertionAttributesUserDetailsService(attributes);
143
service.setConvertToUpperCase(true);
144
return service;
145
}
146
147
@Bean
148
public CasAuthenticationProvider casAuthenticationProvider() {
149
CasAuthenticationProvider provider = new CasAuthenticationProvider();
150
provider.setServiceProperties(serviceProperties());
151
provider.setTicketValidator(ticketValidator());
152
provider.setAuthenticationUserDetailsService(casUserDetailsService());
153
provider.setKey("cas-authentication-provider");
154
return provider;
155
}
156
}
157
```
158
159
### Multiple Attribute Sources
160
161
```java
162
@Bean
163
public AuthenticationUserDetailsService<CasAssertionAuthenticationToken> multiAttributeUserDetailsService() {
164
// Extract authorities from multiple attributes
165
String[] attributes = {"role", "group", "department", "permission"};
166
GrantedAuthorityFromAssertionAttributesUserDetailsService service =
167
new GrantedAuthorityFromAssertionAttributesUserDetailsService(attributes);
168
service.setConvertToUpperCase(true);
169
return service;
170
}
171
```
172
173
### Hybrid Approach with UserDetailsService
174
175
```java
176
@Configuration
177
public class HybridUserDetailsConfig {
178
179
@Bean
180
public CasAuthenticationProvider casAuthenticationProvider() {
181
CasAuthenticationProvider provider = new CasAuthenticationProvider();
182
provider.setServiceProperties(serviceProperties());
183
provider.setTicketValidator(ticketValidator());
184
185
// Use traditional UserDetailsService for basic user loading
186
provider.setUserDetailsService(userDetailsService());
187
188
// Also set assertion-based service for additional attributes
189
provider.setAuthenticationUserDetailsService(casAssertionUserDetailsService());
190
191
provider.setKey("cas-authentication-provider");
192
return provider;
193
}
194
195
@Bean
196
public UserDetailsService userDetailsService() {
197
return new CustomUserDetailsService(); // Your existing implementation
198
}
199
200
@Bean
201
public AuthenticationUserDetailsService<CasAssertionAuthenticationToken> casAssertionUserDetailsService() {
202
String[] attributes = {"additionalRole"};
203
return new GrantedAuthorityFromAssertionAttributesUserDetailsService(attributes);
204
}
205
}
206
```
207
208
## CAS Assertion Structure
209
210
CAS assertions typically contain:
211
212
```java
213
// Example CAS assertion structure
214
Assertion assertion = ...;
215
216
// Principal information
217
AttributePrincipal principal = assertion.getPrincipal();
218
String username = principal.getName(); // Primary identifier
219
220
// User attributes from CAS server
221
Map<String, Object> attributes = principal.getAttributes();
222
List<String> roles = (List<String>) attributes.get("role");
223
String email = (String) attributes.get("email");
224
String fullName = (String) attributes.get("displayName");
225
String department = (String) attributes.get("department");
226
227
// Authentication metadata
228
Date validFromDate = assertion.getValidFromDate();
229
Date validUntilDate = assertion.getValidUntilDate();
230
Map<String, Object> authenticationAttributes = assertion.getAuthenticationAttributes();
231
```
232
233
## Authority Mapping Strategies
234
235
### Simple Role Mapping
236
237
```java
238
// CAS attribute: role = ["admin", "user"]
239
// Result: [ROLE_ADMIN, ROLE_USER] (if convertToUpperCase = true)
240
```
241
242
### Prefix-Based Mapping
243
244
```java
245
public class PrefixedAuthorityUserDetailsService extends AbstractCasAssertionUserDetailsService {
246
247
@Override
248
protected UserDetails loadUserDetails(Assertion assertion) {
249
String username = assertion.getPrincipal().getName();
250
Map<String, Object> attributes = assertion.getPrincipal().getAttributes();
251
252
List<GrantedAuthority> authorities = new ArrayList<>();
253
254
// Add role-based authorities
255
List<String> roles = (List<String>) attributes.get("role");
256
if (roles != null) {
257
roles.forEach(role -> authorities.add(new SimpleGrantedAuthority("ROLE_" + role.toUpperCase())));
258
}
259
260
// Add permission-based authorities
261
List<String> permissions = (List<String>) attributes.get("permission");
262
if (permissions != null) {
263
permissions.forEach(perm -> authorities.add(new SimpleGrantedAuthority("PERM_" + perm.toUpperCase())));
264
}
265
266
return new User(username, "", authorities);
267
}
268
}
269
```
270
271
## Integration Notes
272
273
- **Attribute Configuration**: CAS server must be configured to release required attributes
274
- **Authority Mapping**: Plan authority structure to align with Spring Security's role-based access control
275
- **Performance**: Consider caching user details if assertion processing is expensive
276
- **Fallback Strategy**: Implement fallback when required attributes are missing
277
- **Testing**: Test with various assertion structures and attribute combinations