spring-projects/spring-security-oauth 在這個專案有 OAuth1, OAuth2的 Server, Client實作,在 OAuth1 Client 存取 Server resource時是可以直接存取的,但在 OAuth2 Client的設計是要先登入才能存取 Server resource,如果要直接存取的話,則會出現錯誤如下
org.springframework.security.authentication.InsufficientAuthenticationException: Authentication is required to obtain an access token (anonymous not allowed)
這邊有一邊文章 (
Integrating Google Calendar into a Wicket Application) 可以達到直接存取的目的,
作法大約如下:
1.實作一 class extends AbstractAuthenticationProcessingFilter, 最主要是在
attemptAuthentication 內產生一個 TestingAuthenticationToken,只要是作為 anonymous登入用
public class AnoAuthenicationProcessingFilter extends AbstractAuthenticationProcessingFilter {
private static java.util.logging.Logger log1 = java.util.logging.Logger.getLogger("");
protected AnoAuthenicationProcessingFilter() {
//super內的參數不可為空白
super("/login.jsp");
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
//Authentication authentication = new TestingAuthenticationToken(request.getRemoteAddr(), request.getRemoteAddr(), "ROLE_ANONYMOUS");
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if ( authentication == null) {
log1.log(Level.WARNING,".........establish a new tem Test token for "+ request.getRemoteAddr());
authentication = new TestingAuthenticationToken(request.getRemoteAddr(), request.getRemoteAddr(), "ROLE_ANONYMOUS");
authentication.setAuthenticated(true);
}
return getAuthenticationManager().authenticate(authentication);
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
if (SecurityContextHolder.getContext().getAuthentication() == null) {
SecurityContextHolder.getContext().setAuthentication(attemptAuthentication((HttpServletRequest) req, (HttpServletResponse) res));
if (logger.isDebugEnabled()) {
logger.debug("Populated SecurityContextHolder with dummy token: '"
+ SecurityContextHolder.getContext().getAuthentication() + "'");
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("SecurityContextHolder not populated with dummy token, as it already contained: '"
+ SecurityContextHolder.getContext().getAuthentication() + "'");
}
}
chain.doFilter(req, res);
}
}
2.實作 implements AuthenticationProvider
我希望使用者部份功能需要帳號密碼才能使用,所以在這邊有判斷 Authentication是那一種類
別,在實作 support時就要加上可允許的 token類別
public class CattonOAuthClientProvider implements AuthenticationProvider{
private static java.util.logging.Logger log1 = java.util.logging.Logger.getLogger("");
public CattonOAuthClientProvider() {
super();
}
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
log1.log(Level.WARNING,"authentication class type............. " +authentication.getClass().toString());
if (authentication instanceof UsernamePasswordAuthenticationToken) {
‧‧‧‧‧
‧‧‧‧
log1.log(Level.WARNING, "...login by account password...................");
System.out.println("...login by account password...................");
}else if (authentication instanceof TestingAuthenticationToken ) {
log1.log(Level.WARNING, "...anonymous..............");
} else {
log1.log(Level.WARNING, "faile to authenicate in provder..............");
}
return authentication;
}
public boolean supports(Class authentication) {
return TestingAuthenticationToken.class.isAssignableFrom(authentication)
|| UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
}
3.修改 spring-servlet.xml
在 custom-filter after="EXCEPTION_TRANSLATION_FILTER" ref="oauth2ClientFilter" 之前加上
custom-filter before="ANONYMOUS_FILTER" ref="authProcessingFilter"
之後再加上
bean class="com.catton.spring.security.AnoAuthenicationProcessingFilter" id="authProcessingFilter"
property name="authenticationManager" ref="defaultAuthenticationManager"
bean id="authenticationProvider" class="com.catton.spring.security.provider.CattonOAuthClientProvider"
最後再修改 authentication-manager 設定為 authentication-provider ref="authenticationProvider" 即
可