I am trying to create an application that will mainly access the REST API using Spring, and am trying to configure security. Trying to present the actual structure of the application using this picture:
- The request can come from any platform at "abc.com/rest_api/"
- The request will be sent to point 3 or point 5. If the user is already authenticated by username and password, the request will be checked against Token else, which will be redirected to the database.
- If the username and password must be authenticated by the database, then a token will be issued and sent back in response.
- After that, only token-based authentication will work.
, , , - .
@Configuration
@EnableWebSecurity
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(securedEnabled=true, prePostEnabled=true)
public class UserDetailsSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private NSecurityContextHolder securityContextHolder;
@Autowired
private NHttpServletRequestBinder<Authentication> authenticationBinder;
public static final String DEF_USERS_BY_USERNAME_QUERY
= "SELECT user ";
public static final String GROUPS_BY_USERNAME_QUERY =
"SELECT groups by user";
public static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY =
"SELECT authorities";
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(getDataSourceFromJndi())
.usersByUsernameQuery(DEF_USERS_BY_USERNAME_QUERY).
authoritiesByUsernameQuery(DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY).
groupAuthoritiesByUsername(GROUPS_BY_USERNAME_QUERY);
}
private DataSource getDataSourceFromJndi() {
try {
DataSource dataSource = (DataSource) new InitialContext().lookup("DS");
return dataSource;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(getDataSourceFromJndi())
.usersByUsernameQuery(DEF_USERS_BY_USERNAME_QUERY).
authoritiesByUsernameQuery(DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY).
groupAuthoritiesByUsername(GROUPS_BY_USERNAME_QUERY);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
final SimpleUrlAuthenticationSuccessHandler delegate = new SimpleUrlAuthenticationSuccessHandler();
delegate.setDefaultTargetUrl("/api/");
http.sessionManagement().sessionCreationPolicy(STATELESS);
http.csrf().disable();
http.authorizeRequests().anyRequest().authenticated();
http
.formLogin().loginPage("http://localhost/web/ui/#access/signin")
.permitAll()
.successHandler(new NStatelessAuthenticationSuccessHandler(authenticationBinder, delegate));
http.logout().logoutUrl("http://localhost/web/ui/#access/signin").logoutSuccessUrl("http://localhost/web/ui/#access/signin");
http.addFilterBefore(
new StatelessAuthenticationFilter(authenticationBinder, securityContextHolder),
UsernamePasswordAuthenticationFilter.class
);
}
}
Binder, TokenBased UserNameBased.
TokenBased:
@Component
public class NXAuthTokenHttpServletRequestBinder implements NHttpServletRequestBinder<String> {
private static final String X_AUTH_TOKEN = "X-AUTH-TOKEN";
private final NTokenFactory tokenFactory;
@Autowired
public NXAuthTokenHttpServletRequestBinder(NTokenFactory tokenFactory) {
this.tokenFactory = tokenFactory;
}
@Override
public void add(HttpServletResponse response, String username) {
final String token = tokenFactory.create(username);
response.addHeader(X_AUTH_TOKEN, token);
response.addCookie(new Cookie(X_AUTH_TOKEN, token));
}
@Override
public String retrieve(HttpServletRequest request) {
final String cookieToken = findToken(request);
if (cookieToken != null) {
return tokenFactory.parseUsername(cookieToken);
}
return null;
}
private static String findToken(HttpServletRequest request) {
Enumeration<String> it = request.getHeaderNames();
while(it.hasMoreElements()){
System.out.println(it.nextElement());
}
final String headerToken = request.getHeader(X_AUTH_TOKEN);
if (headerToken != null) {
return headerToken;
}
final Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (X_AUTH_TOKEN.equals(cookie.getName())) {
return cookie.getValue();
}
}
}
return null;
}
}
UserBased:
@Component
@Primary
public class NUserAuthenticationFactory implements NHttpServletRequestBinder<Authentication> {
private final NHttpServletRequestBinder<String> httpServletRequestBinder;
@Autowired
public NUserAuthenticationFactory(NHttpServletRequestBinder<String> httpServletRequestBinder) {
this.httpServletRequestBinder = httpServletRequestBinder;
}
@Override
public void add(HttpServletResponse response, Authentication authentication) {
httpServletRequestBinder.add(response, authentication.getName());
}
@Override
public UserAuthentication retrieve(HttpServletRequest request) {
final String username = httpServletRequestBinder.retrieve(request);
if (username != null) {
return new UserAuthentication(new CustomJDBCDaoImpl().loadUserByUsername(username));
}
return null;
}
}
, , UserBased Authentication, . , , . .
Logs:
:/ 1 12 ; : "WebAsyncManagerIntegrationFilter" :/ 2 12 ; : "SecurityContextPersistenceFilter" :/ 3 12 ; firing Filter: "HeaderWriterFilter" Fine:
HSTS, Matcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@a837508 :/ 4 12 ; : "LogoutFilter" Fine: : '/'; http://localhost/web/ui/#access/signin ':/ 5 12 ; : "StatelessAuthenticationFilter" Fine:/ 6 12 ; : "UsernamePasswordAuthenticationFilter" Fine: "GET/" match 'POST http://localhost/web/ui/#access/signin :/at 7 12 ; : "RequestCacheAwareFilter" Fine:/ 8 12 ; firing Filter: 'SecurityContextHolderAwareRequestFilter' :/ 9 12 ; : " " : SecurityContextHolder : "Org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: : anonymousUser; : []; : ; : org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; : ROLE_ANONYMOUS 'Fine:/ 10 12 ; firing Filter:" SessionManagementFilter" Fine: Requested ID 3e2c15a2a427bf47e51496d2a186 . :/at 11 12 ; : 'ExceptionTranslationFilter' :/ 12 12 ; firing Filter: 'FilterSecurityInterceptor' Fine: : FilterInvocation: URL:/; : [authenticated] Fine: : org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: : anonymousUser; : []; : ; : org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; : ROLE_ANONYMOUS Fine: : org.springframework.security.web.access.expression.WebExpressionVoter@2ac71565, return: -1 Fine: ( ); org.springframework.security.access.AccessDeniedException: