參考文件:
Spring OAuth 2 Developers Guide
如果你有很多系統但想用同一個方式登入,存取資源的話,建立 OAuth2 Server是一個好方法,可以參考 spring-security-oauth的方式來建立。 但目前很多範例是用 Spring Boot來建立,我參考的 Secure Spring REST With Spring Security and OAuth2 (github source code) 就是。
幾個點跟大家分享:
儲存 token, user帳號、宓碼會有三種方式
1.InMemory的方式,也是預設的方式
2.把資訊記錄在資料庫內,使用 jdbc data store,參考的範例內有 H2和 Postgresql,其它分享有用 MySQL的方式,如果是用 H2的話,一開始應該會放在本機,如果要用 remote模式要參考 Allow remote access to H2 Database設定,宓碼變更請參考 set password
3.jwt方式,spring security 5.1開始就只支援這種方式
文章有提到會建立 Authorization Server, Resource Server,實際上是建立相關的 configuration class設定的動作,基本上有三個,也可以只寫一個 class extends 三個類別
2019年1月29日 星期二
2014年5月28日 星期三
Spring Security OAuth2 Client anonymous 存取 Oauth2 resource
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" 即
可
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" 即
可
2012年11月6日 星期二
spring security oauth 1 client logout
這樣應該就可以作 logout的動作
OAuthSecurityContext context = OAuthSecurityContextHolder.getContext();
Map
accessTokens.remove(resource id);
session.removeAttribute("OAUTH_TOKEN#"+resourceid);
參考文件
http://forum.springsource.org/showthread.php?104433-Revoked-expired-access-token-in-oauth1
2012年8月27日 星期一
OAuth for Spring Security 應用實作
參考來源:https://github.com/SpringSource/spring-security-oauth/wiki/tutorial
程式下載:https://github.com/SpringSource/spring-security-oauth 網頁下面有文字說明如何使用 maven將 source code下載回來。
目前 tutorial 有實作 OAuth1, OAuth2 的Server 和 Client, Client 可以下載 Server 的圖片。 而我的目的就是可以建立一個公司內部認證網站(OAuth 是用來作授權用的),使用者在連其它公司自製網站時可以連來這個網站使用 Notes or AD, Oracle ERP 的帳號作登入,當帳號、密碼正確時就可以允許 oauth client 存取 登入使用者的工號、部門、 email等。
修改部份:
首先我選擇的是拿 OAuth2的範列來改,Server 的 source code 代號為 Sparklr2,Client 的代號為 Tonr2。
因為要讓使用者可以用Notes or AD, Oracle ERP 的帳號作登入,所以要先改 OAuth2 Server 的部份,目前 spring security 選擇 authentication的方式有兩種,一種是 AuthenticationProvider ,一種是UserService,我選擇的是AuthenticationProvider ,所以要實作一個 class implements org.springframework.security.authentication.AuthenticationProvider 並實作兩個 method
1.public Authentication authenticate(Authentication authentication) ==>讓我可以用Notes or AD, Oracle ERP 的帳號、宓碼登入後檢查是否正確。
2. public boolean supports(Class authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
等這個 provider實作好之後,就要在 WEB-INF\spring-servlet.xml 內加上設定,這樣就可以完成認證的功能了。
再來就是分享員工資訊的程式了, 可以實作自己的 controller,參考原程式分享 photoID的 service : PhotoController, PhotoServiceImpl,PhotoService
記得WEB-INF\spring-servlet.xml 也要將相關的 tag 加上。
而 Client 的程式也請參考原程式如何存取 photoID
等 server , client 的程式都實作好之後就可以放到 tomcat 上了。 目前實作好之後才發現 Client 要先自己認證完成,才可以去 Server再去作認證一次,才可以存取我的員工資訊,但我想的是 Client 本身不用有認證的程序,直接到 server 作認證後存取資料。上網找了一下資訊,發現好像沒有人分享怎麼設定 OAuth2才可以達到我的要求,所以我又拿了 OAuth1的 source code改了一次。
OAuth1 跟 OAuth2 設定上有一點不一樣,除了WEB-INF\spring-servlet.xml之外,又多了一個檔案 applicationContext.xml,也是 spring 標準設定檔。
這邊也是實作我自己的 AuthenticationProvider ,但是在設定上就不是在 spring-servlet.xml新增的資訊了,而是要放在 applicationContext.xml ,而且 bean 要改為 beans:bean 這樣才可以。 其它 controller or service的程式才在spring-servlet.xml 註冊。
再來就是 oauth:consumer-details-service id="consumerDetails" 的設定
程式下載:https://github.com/SpringSource/spring-security-oauth 網頁下面有文字說明如何使用 maven將 source code下載回來。
目前 tutorial 有實作 OAuth1, OAuth2 的Server 和 Client, Client 可以下載 Server 的圖片。 而我的目的就是可以建立一個公司內部認證網站(OAuth 是用來作授權用的),使用者在連其它公司自製網站時可以連來這個網站使用 Notes or AD, Oracle ERP 的帳號作登入,當帳號、密碼正確時就可以允許 oauth client 存取 登入使用者的工號、部門、 email等。
修改部份:
首先我選擇的是拿 OAuth2的範列來改,Server 的 source code 代號為 Sparklr2,Client 的代號為 Tonr2。
因為要讓使用者可以用Notes or AD, Oracle ERP 的帳號作登入,所以要先改 OAuth2 Server 的部份,目前 spring security 選擇 authentication的方式有兩種,一種是 AuthenticationProvider ,一種是UserService,我選擇的是AuthenticationProvider ,所以要實作一個 class implements org.springframework.security.authentication.AuthenticationProvider 並實作兩個 method
1.public Authentication authenticate(Authentication authentication) ==>讓我可以用Notes or AD, Oracle ERP 的帳號、宓碼登入後檢查是否正確。
2. public boolean supports(Class authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
等這個 provider實作好之後,就要在 WEB-INF\spring-servlet.xml 內加上設定,這樣就可以完成認證的功能了。
再來就是分享員工資訊的程式了, 可以實作自己的 controller,參考原程式分享 photoID的 service : PhotoController, PhotoServiceImpl,PhotoService
記得WEB-INF\spring-servlet.xml 也要將相關的 tag 加上。
而 Client 的程式也請參考原程式如何存取 photoID
等 server , client 的程式都實作好之後就可以放到 tomcat 上了。 目前實作好之後才發現 Client 要先自己認證完成,才可以去 Server再去作認證一次,才可以存取我的員工資訊,但我想的是 Client 本身不用有認證的程序,直接到 server 作認證後存取資料。上網找了一下資訊,發現好像沒有人分享怎麼設定 OAuth2才可以達到我的要求,所以我又拿了 OAuth1的 source code改了一次。
OAuth1 跟 OAuth2 設定上有一點不一樣,除了WEB-INF\spring-servlet.xml之外,又多了一個檔案 applicationContext.xml,也是 spring 標準設定檔。
這邊也是實作我自己的 AuthenticationProvider ,但是在設定上就不是在 spring-servlet.xml新增
再來就是 oauth:consumer-details-service id="consumerDetails" 的設定
這邊就可增加自己 oauth:consumer
實作分享員工資訊的程式也跟 OAuth2實作差不多,設定上 OAuth1 OAuth2會有差異,只要參考原程式存取 photoID怎麼寫和怎麼設定應該就沒什麼大問題了。
最後我想測試用 asp.net 也可以存取我的 OAuth1實作出來的 server員工資訊,這一次我採用
DotNetOpenAuth http://www.dotnetopenauth.net/
實作自己的 service comsumer
public static readonly ServiceProviderDescription ServiceDescription = new ServiceProviderDescription {
RequestTokenEndpoint = new MessageReceivingEndpoint("http://ip:port/OAuth1Server/oauth/request_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
UserAuthorizationEndpoint = new MessageReceivingEndpoint("http://ip:port/OAuth1Server /oauth/confirm_access", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
AccessTokenEndpoint = new MessageReceivingEndpoint("http:// ip:port/OAuth1Server /oauth/access_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
};
/// The URI to get the data on the user's home page.
///
private static readonly MessageReceivingEndpoint cattonEmpInfoEndpoint = new MessageReceivingEndpoint("http:// ip:port/OAuth1Server/service?format=json", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest);
private static InMemoryTokenManager ShortTermUserSessionTokenManager {
get {
var store = HttpContext.Current.Session;
var tokenManager = (InMemoryTokenManager)store["CattonOAuth1SessionTokenManager"];
if (tokenManager == null) {
string consumerKey = ConfigurationManager.AppSettings["CattonOAuthConsumerKey"];
string consumerSecret = ConfigurationManager.AppSettings["CattonOAuthConsumerSecret"];
if (IsTwitterConsumerConfigured) {
tokenManager = new InMemoryTokenManager(consumerKey, consumerSecret);
store["CattonOAuth1SessionTokenManager"] = tokenManager;
} else {
throw new InvalidOperationException("No Catton OAuth consumer key and secret could be found in web.config AppSettings.");
}
}
return tokenManager;
}
}
public static string GetUserInfo(ConsumerBase cattonOAuthCB, string accessToken) {
IncomingWebResponse response = cattonOAuthCB.PrepareAuthorizedRequestAndSend(cattonEmpInfoEndpoint, accessToken);
return response.GetResponseReader().ReadToEnd();
}
實作畫面
訂閱:
文章 (Atom)