/
LemonSecurityConfig.java
269 lines (220 loc) · 8.18 KB
/
LemonSecurityConfig.java
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
package com.naturalprogrammer.spring.lemon.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
import org.springframework.security.web.authentication.switchuser.SwitchUserFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import com.naturalprogrammer.spring.lemon.LemonProperties;
/**
* Security configuration class. Extend it in the
* application, and make a configuration class. Override
* protected methods, if you need any customization.
*
* @author Sanjay Patel
*/
@EnableGlobalMethodSecurity(prePostEnabled = true)
public abstract class LemonSecurityConfig extends WebSecurityConfigurerAdapter {
// Computed authorities
public static final String GOOD_ADMIN = "GOOD_ADMIN";
public static final String GOOD_USER = "GOOD_USER";
// remember-me related
public static final String REMEMBER_ME_COOKIE = "rememberMe";
public static final String REMEMBER_ME_PARAMETER = "rememberMe";
protected LemonProperties properties;
protected UserDetailsService userDetailsService;
protected AuthenticationSuccessHandler authenticationSuccessHandler;
protected LogoutSuccessHandler logoutSuccessHandler;
// name of the header to be receiving from the client
public static final String XSRF_TOKEN_HEADER_NAME = "X-XSRF-TOKEN";
// name of the cookie
public static final String XSRF_TOKEN_COOKIE_NAME = "XSRF-TOKEN";
@Autowired
public void setProperties(LemonProperties properties) {
this.properties = properties;
}
@Autowired
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Autowired
public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {
this.authenticationSuccessHandler = authenticationSuccessHandler;
}
@Autowired
public void setLogoutSuccessHandler(LogoutSuccessHandler logoutSuccessHandler) {
this.logoutSuccessHandler = logoutSuccessHandler;
}
/**
* Authentication failure handler, to override the default behavior
* of spring security - redirecting to the login screen
*/
@Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
return new SimpleUrlAuthenticationFailureHandler();
}
/**
* Password encoder
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* Security configuration, calling protected methods
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
login(http); // authentication
logout(http); // logout related configuration
exceptionHandling(http); // exception handling
rememberMe(http); // remember-me
csrf(http); // csrf configuration
switchUser(http); // switch-user configuration
authorizeRequests(http); // authorize requests
otherConfigurations(http); // override this to add more configurations
}
/**
* Configuring authentication.
*
* @param http
* @throws Exception
*/
protected void login(HttpSecurity http) throws Exception {
http
.formLogin() // cookie based form login
/******************************************
* Setting a successUrl would redirect the user there. Instead,
* let's send 200 and the userDto.
*****************************************/
.successHandler(authenticationSuccessHandler)
/*******************************************
* Setting the failureUrl will redirect the user to
* that url if login fails. Instead, we need to send
* 401. So, let's set failureHandler instead.
*******************************************/
.failureHandler(authenticationFailureHandler());
}
/**
* Logout related configuration
*
* @param http
* @throws Exception
*/
protected void logout(HttpSecurity http) throws Exception {
http
.logout()
/************************************************
* To prevent redirection to home page, we need to
* have this custom logoutSuccessHandler
***********************************************/
.logoutSuccessHandler(logoutSuccessHandler)
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID");
}
/**
* Configures exception-handling
*
* @param http
* @throws Exception
*/
protected void exceptionHandling(HttpSecurity http) throws Exception {
http
.exceptionHandling()
/***********************************************
* To prevent redirection to the login page
* when someone tries to access a restricted page
**********************************************/
.authenticationEntryPoint(new Http403ForbiddenEntryPoint());
}
/**
* Configures remember-me
*
* @param http
* @throws Exception
*/
protected void rememberMe(HttpSecurity http) throws Exception {
http
.rememberMe()
.key(properties.getRememberMeKey())
.rememberMeServices(rememberMeServices());
}
/**
* Configures CSRF
*
* @param http
* @throws Exception
*/
protected void csrf(HttpSecurity http) throws Exception {
http
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
/**
* Adds switch-user filter
*
* @param http
*/
protected void switchUser(HttpSecurity http) {
http
.addFilterAfter(switchUserFilter(), FilterSecurityInterceptor.class);
}
/**
* URL based authorization configuration. Override this if needed.
*
* @param http
* @throws Exception
*/
protected void authorizeRequests(HttpSecurity http) throws Exception {
http.authorizeRequests()
.mvcMatchers("/login/impersonate*").hasRole(GOOD_ADMIN)
.mvcMatchers("/logout/impersonate*").authenticated()
.mvcMatchers("/**").permitAll();
}
/**
* Override this to add more http configurations,
* such as more authentication methods.
*
* @param http
* @throws Exception
*/
protected void otherConfigurations(HttpSecurity http) throws Exception {
}
/**
* Override this method if you want to
* setup a different RememberMeServices
*
* @return
*/
protected AbstractRememberMeServices rememberMeServices() {
TokenBasedRememberMeServices rememberMeServices =
new TokenBasedRememberMeServices
(properties.getRememberMeKey(), userDetailsService);
rememberMeServices.setParameter(REMEMBER_ME_PARAMETER); // default is "remember-me" (in earlier spring security versions it was "_spring_security_remember_me")
rememberMeServices.setCookieName(REMEMBER_ME_COOKIE);
return rememberMeServices;
}
/**
* Returns switch-user filter
*
* @return
*/
protected SwitchUserFilter switchUserFilter() {
SwitchUserFilter filter = new SwitchUserFilter();
filter.setUserDetailsService(userDetailsService);
filter.setSuccessHandler(authenticationSuccessHandler);
filter.setFailureHandler(authenticationFailureHandler());
return filter;
}
}