我正在嘗試創建一個應用程序,它將主要使用Spring訪問REST API,並且正在嘗試配置安全性方面。設法提出使用該畫面中的應用程序的實際結構: Spring安全與REST API
- 請求可以來自任何平臺,以「abc.com/rest_api/」
- 請求將被髮送到第3點或點5。如果用戶已經通過用戶名和密碼進行了認證,那麼請求將根據Token進行驗證,否則將被重定向到數據庫。
- 如果用戶名和密碼必須通過數據庫進行身份驗證,則會生成一個令牌並作爲響應發回。
- 之後,只有基於令牌的認證才能起作用。
我試圖創建一個基本的結構,我認爲必須做出一個小錯誤,因爲它不按預期工作。
@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 {
// The http.formLogin().defaultSuccessUrl("/path/") method is required when using stateless Spring Security
// because the session cannot be used to redirect to the page that was requested while signed out. Unfortunately
// using this configuration method will cause our custom success handler (below) to be overridden with the
// default success handler. So to replicate the defaultSuccessUrl("/path/") configuration we will instead
// correctly configure and delegate to the default success handler.
final SimpleUrlAuthenticationSuccessHandler delegate = new SimpleUrlAuthenticationSuccessHandler();
delegate.setDefaultTargetUrl("/api/");
// Make Spring Security stateless. This means no session will be created by Spring Security, nor will it use any
// previously existing session.
http.sessionManagement().sessionCreationPolicy(STATELESS);
// Disable the CSRF prevention because it requires the session, which of course is not available in a
// stateless application. It also greatly complicates the requirements for the sign in POST request.
http.csrf().disable();
// Viewing any page requires authentication.
http.authorizeRequests().anyRequest().authenticated();
http
.formLogin().loginPage("http://localhost/web/ui/#access/signin")
.permitAll()
// Override the sign in success handler with our stateless implementation. This will update the response
// with any headers and cookies that are required for subsequent authenticated requests.
.successHandler(new NStatelessAuthenticationSuccessHandler(authenticationBinder, delegate));
http.logout().logoutUrl("http://localhost/web/ui/#access/signin").logoutSuccessUrl("http://localhost/web/ui/#access/signin");
// Add our stateless authentication filter before the default sign in filter. The default sign in filter is
// still used for the initial sign in, but if a user is authenticated we need to acknowledge this before it is
// reached.
http.addFilterBefore(
new StatelessAuthenticationFilter(authenticationBinder, securityContextHolder),
UsernamePasswordAuthenticationFilter.class
);
}
}
而且我有兩種類型的authenticationBinder,即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;
}
}
基於用戶:
@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;
}
}
問題 每當我打開我的應用涉及到基於用戶的身份驗證,然後嘗試從令牌,而不是獲取用戶名從數據庫驗證它。但是到那個時候沒有任何令牌,因爲這是我從UI製作的第一個發佈請求。它會將我重定向到相同的登錄頁面。
日誌:
精細:/在額外的過濾器鏈12的第1位;射擊 過濾器:'WebAsyncManagerIntegrationFilter'罰款:/在位置2的 12在額外的過濾鏈中;點火過濾器: 'SecurityContextPersistenceFilter'罰款:/位於12的第3位 附加過濾器鏈;發射過濾器:'HeaderWriterFilter'罰款:
不注入HSTS頭,因爲它與requestMatcher org.springframework.security.web.header.writers不匹配。HstsHeaderWriter $ SecureRequestMatcher @ a837508 Fine:/位於12的第4位,位於額外的過濾器鏈中;射擊 過濾器:'註銷過濾器'罰款:檢查請求匹配:'/'; 針對'http://localhost/web/ui/#access/signin'罰款:/位置 5 of 12 in additional filter chain;燃燒過濾器: 'StatelessAuthenticationFilter'罰款:/位於12的第6位 額外的過濾器鏈;發射過濾器: 'UsernamePasswordAuthenticationFilter'罰款:請求'GET /'不與 匹配'POST http://localhost/web/ui/#access/signin罰款:/在 位置7的12在另外的過濾器鏈中;點火過濾器: 'RequestCacheAwareFilter'罰款:/位於第12位的另外 過濾器鏈;發射濾波器:'SecurityContextHolderAwareRequestFilter' 精細:/位於附加濾波器鏈12的位置9;射擊 過濾器:'AnonymousAuthenticationFilter'罰款:已填充 SecurityContextHolder with anonymous token: 'org.sprin[email protected]9055e4a6: Principal:anonymousUser;證書:[PROTECTED];已認證: true;詳細信息: org.sprin[email protected]957e: RemoteIpAddress:127.0.0.1; SessionId:null;授予的權限: ROLE_ANONYMOUS'好:/在第12位的第10位附加過濾器 鏈;射擊過濾器:'SessionManagementFilter'罰款:請求 會話ID 3e2c15a2a427bf47e51496d2a186無效。精細:/在 位置11的12在另外的過濾器鏈中;燃燒過濾器: 'ExceptionTranslationFilter'罰款:/位於12的12位 附加過濾鏈; 「FilterSecurityInterceptor」 Fine:Secure object:FilterInvocation:URL:/;屬性: [已驗證]正常:以前已通過身份驗證: org.sprin[email protected]9055e4a6: 負責人:anonymousUser;證書:[PROTECTED];已認證: true;詳細信息: org.sprin[email protected]957e: RemoteIpAddress:127.0.0.1; SessionId:null;授予權限: ROLE_ANONYMOUS罰款:選民: org.sp[email protected]2ac71565, 返回:-1罰款:訪問被拒絕(用戶是匿名的);重定向 到身份驗證入口點 org.springframework.security.access.AccessDeniedException:訪問被拒絕