明点软件 avatar 明点软件 committed 5cd7cf8

合并spring security taobao的代码

Comments (0)

Files changed (14)

SpringSecurityTaobaoGrailsPlugin.groovy

+/* Copyright 2006-2011 the original author or authors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
 import java.util.List
 
 import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils
 import org.codehaus.groovy.grails.plugins.springsecurity.SecurityFilterPosition
 import com.mingidea.security.taobao.*
 
+/**
+* @author <a href='mailto:simon.r.leung@gmail.com'>Simon Leung</a>
+*/
 class SpringSecurityTaobaoGrailsPlugin {
     // the plugin version
-    def version = "0.8.1"
+    def version = "0.8.2"
     // the version or versions of Grails the plugin is designed for
-    def grailsVersion = "1.3.7 > *"
+    def grailsVersion = "2.0 > *"
     // the other plugins this plugin depends on
     def dependsOn = [springSecurityCore: '1.2.7 > *']
     // resources that are excluded from plugin packaging
         'grails-app/controllers/**',
         'grails-app/views/**',
         'docs/**',
-        'src/docs/**'
+        'src/docs/**',
+		'src/groovy/**'
     ]
 
     def author = "Simon Leung"
     def authorEmail = "simon.r.leung@gmail.com"
-    def title = "Taobao open authentication support for the Spring Security plugin."
-    def description = "Taobao open authentication support for the Spring Security plugin."
+    def title = "Spring Security Taobao Plugin"
+    def description = "Taobao open api authentication support for the Spring Security plugin."
+
+	def license = "APACHE"
+	def developers = [
+	        [ name: "Simon Leung", email: "simon.r.leung@gmail.com" ]
+		]
+	def issueManagement = [ system: "bitbucket", url: "https://bitbucket.org/mingidea/grails-spring-security-taobao/issues" ]
+	def scm = [ url: "https://bitbucket.org/mingidea/grails-spring-security-taobao" ]
 
     // URL to the plugin's documentation
     def documentation = "http://grails.org/plugin/spring-security-taobao"

application.properties

 #Grails Metadata file
-#Sun Jan 15 14:41:39 CST 2012
+#Thu Feb 02 11:38:52 CST 2012
 app.grails.version=2.0.0
 app.name=spring-security-taobao
 plugins.hibernate=2.0.0
+plugins.release=1.0.1
 plugins.spring-security-core=1.2.7
 plugins.tomcat=2.0.0

grails-app/conf/BuildConfig.groovy

     log "warn" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
     repositories {
         grailsPlugins()
-        grailsHome()
-        grailsCentral()
+		grailsHome()
+		grailsCentral()
 
-        ebr() // SpringSource  http://www.springsource.com/repository
-        // uncomment the below to enable remote dependency resolution
-        // from public Maven repositories
-        mavenLocal()
-        mavenCentral()
-        mavenRepo "http://snapshots.repository.codehaus.org"
-        mavenRepo "http://repository.codehaus.org"
-        mavenRepo "http://download.java.net/maven/2/"
-        mavenRepo "http://repository.jboss.com/maven2/"
+		mavenCentral()
     }
     dependencies {
         // specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes eg.
         runtime('commons-codec:commons-codec:1.5'){
             transitive:false
         }
-        runtime ':spring-security-taobao:0.2'
     }
 }
Add a comment to this file

lib/spring-security-taobao-0.2.jar

Binary file removed.

-<plugin name='spring-security-taobao' version='0.8.1' grailsVersion='1.3.7 &gt; *'>
+<plugin name='spring-security-taobao' version='0.8.2' grailsVersion='2.0 &gt; *'>
   <author>Simon Leung</author>
   <authorEmail>simon.r.leung@gmail.com</authorEmail>
-  <title>Taobao open authentication support for the Spring Security plugin.</title>
-  <description>Taobao open authentication support for the Spring Security plugin.</description>
+  <title>Spring Security Taobao Plugin</title>
+  <description>Taobao open api authentication support for the Spring Security plugin.</description>
   <documentation>http://grails.org/plugin/spring-security-taobao</documentation>
   <type>SpringSecurityTaobaoGrailsPlugin</type>
   <resources>
     <repository name='http://repo.grails.org/grails/plugins' url='http://repo.grails.org/grails/plugins/' />
     <repository name='http://repo.grails.org/grails/core' url='http://repo.grails.org/grails/core/' />
     <repository name='grailsCore' url='http://svn.codehaus.org/grails/trunk/grails-plugins' />
-    <repository name='ebrRelease' url='http://repository.springsource.com/maven/bundles/release/' />
-    <repository name='ebrExternal' url='http://repository.springsource.com/maven/bundles/external/' />
     <repository name='mavenCentral' url='http://repo1.maven.org/maven2/' />
-    <repository name='http://snapshots.repository.codehaus.org' url='http://snapshots.repository.codehaus.org/' />
-    <repository name='http://repository.codehaus.org' url='http://repository.codehaus.org/' />
-    <repository name='http://download.java.net/maven/2/' url='http://download.java.net/maven/2/' />
-    <repository name='http://repository.jboss.com/maven2/' url='http://repository.jboss.com/maven2/' />
   </repositories>
   <dependencies>
     <runtime>
       <dependency group='commons-codec' name='commons-codec' version='1.5' />
-      <dependency group='' name='spring-security-taobao' version='0.2' />
     </runtime>
   </dependencies>
   <plugins />

src/java/com/mingidea/security/taobao/PrincipalNotFoundException.java

+package com.mingidea.security.taobao;
+
+import org.springframework.security.core.AuthenticationException;
+
+public class PrincipalNotFoundException extends AuthenticationException {
+	private static final long serialVersionUID = 1L;
+
+	public PrincipalNotFoundException(String msg) {
+		super(msg);
+	}
+}

src/java/com/mingidea/security/taobao/TaobaoAuthenticationDao.java

+package com.mingidea.security.taobao;
+
+import org.springframework.security.core.userdetails.UserDetails;
+
+public interface TaobaoAuthenticationDao {
+	/**
+	 * 
+	 * @param nick 淘宝用户昵称
+	 * @return 如果不存在返回null
+	 */
+	public TaobaoUser find(String nick, String appKey);
+
+	public void update(TaobaoUser taobaoUser);
+
+	public void create(TaobaoUser taobaoUser);
+
+	public UserDetails getPrincipal(TaobaoUser taobaoUser);
+}

src/java/com/mingidea/security/taobao/TaobaoAuthenticationProcessingFilter.java

+package com.mingidea.security.taobao;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
+
+public class TaobaoAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
+
+    public TaobaoAuthenticationProcessingFilter() {
+        super("/j_spring_taobao_security_check");
+    }
+
+    @Override
+    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
+        throws AuthenticationException, IOException, ServletException {
+        TaobaoCredentials credentials = TaobaoCredentials.build(request);
+        
+        TaobaoAuthenticationToken token = new TaobaoAuthenticationToken(credentials);
+        // delegate to the authentication provider
+        Authentication authentication = this.getAuthenticationManager().authenticate(token);
+
+        return authentication;
+    }
+}

src/java/com/mingidea/security/taobao/TaobaoAuthenticationProvider.java

+package com.mingidea.security.taobao;
+
+import java.util.Map;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.CredentialsExpiredException;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.util.Assert;
+
+public class TaobaoAuthenticationProvider implements AuthenticationProvider, InitializingBean, ApplicationContextAware  {
+    private final static Logger log = LoggerFactory.getLogger(TaobaoAuthenticationProvider.class);
+    
+    private ApplicationContext applicationContext;
+    
+    private TaobaoAuthenticationDao taobaoAuthenticationDao;
+    
+    /** appKey to appSecret mapping */
+    private Map<String, String> appSecretMap;
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        Assert.notNull(taobaoAuthenticationDao, "The taobaoAuthenticationDao property can't be null");
+        Assert.notNull(appSecretMap, "The appSecretMap property can't be null");
+    }
+
+    @Override
+    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+        if (!supports(authentication.getClass())) {
+            return null;
+        }
+
+        TaobaoAuthenticationToken token = (TaobaoAuthenticationToken) authentication;
+        TaobaoCredentials credentials = (TaobaoCredentials) token.getCredentials();
+        validateCredentials(credentials);
+        
+        // 淘宝用户昵称
+        String nick = credentials.getVisitorNick();
+        String appKey = credentials.getAppKey();
+        String session = credentials.getSession();
+        
+        TaobaoUser taobaoUser = taobaoAuthenticationDao.find(nick, appKey);
+        if(taobaoUser == null) {
+        	log.debug("create taobao user {}", nick);
+        	taobaoUser = new TaobaoUser(nick, appKey, session);
+        	taobaoAuthenticationDao.create(taobaoUser);
+        	applicationContext.publishEvent(new TaobaoUserCreatedEvent(this, taobaoUser));
+        } else {
+        	if(taobaoUser.getSession() != session) {
+        		log.debug("update taobao user {} with session {}", nick, session);
+        		taobaoUser.setSession(session);
+        		taobaoAuthenticationDao.update(taobaoUser);
+        		applicationContext.publishEvent(new TaobaoUserUpdatedEvent(this, taobaoUser));
+        	}
+        }
+        
+        UserDetails userDetails = taobaoAuthenticationDao.getPrincipal(taobaoUser);
+        
+        if(userDetails == null) {
+            throw new PrincipalNotFoundException("can not found principal for taobao user [" + nick + "]");
+        } else {
+        	//remove credentials
+        	return new TaobaoAuthenticationToken(userDetails, null, userDetails.getAuthorities());
+        }
+    }
+
+    /**
+     * 
+     * @throws BadCredentialsException credentials is invalid
+     */
+    private void validateCredentials(TaobaoCredentials credentials) throws BadCredentialsException {
+        String appkey = credentials.getAppKey();
+        String parameters = credentials.getParameters();
+        String session = credentials.getSession();
+        String sign = credentials.getSign();
+        validateSign(appkey, parameters, session, sign);
+
+        validateTime(credentials.getTimestamp());
+    }
+
+    /**
+     * 签名规则为base64(md5(top_appkey+top_parameters+top_session+app_secret))
+     * 
+     * @throws CredentialsExpiredException
+     */
+    private void validateSign(String appkey, String parameters, String session, String sign)
+        throws BadCredentialsException {
+        String appSecret = this.appSecretMap.get(appkey);
+        if (appSecret == null) {
+            log.error("The corresponding App Secret can't be found. appKey: {}", appkey);
+            throw new BadCredentialsException("The corresponding App Secret can't be found");
+        }
+        byte[] md5 = DigestUtils.md5(appkey + parameters + session + appSecret);
+        // apache commons codec的版本要在1.5及以上,否则计算出来的签名会多一个回车导致异常
+        String calculatedSign = Base64.encodeBase64String(md5);
+        if (!calculatedSign.equals(sign)) {
+            throw new BadCredentialsException("The sign is invalid: " + sign);
+        }
+    }
+
+    /**
+     * 请求时间不能和当前时间超过30分钟
+     * 
+     * @param timestamp
+     * @throws BadCredentialsException
+     */
+    private void validateTime(long timestamp) throws BadCredentialsException {
+        long now = System.currentTimeMillis();
+        if ((now - timestamp) > 1800000) { // 30分钟
+            throw new CredentialsExpiredException(
+                "The difference between the request time and the server's time is too large");
+        }
+    }
+    
+    public void setTaobaoAuthenticationDao(TaobaoAuthenticationDao taobaoAuthenticationDao) {
+		this.taobaoAuthenticationDao = taobaoAuthenticationDao;
+	}
+
+	public void setAppSecretMap(Map<String, String> appSecretMap) {
+        this.appSecretMap = appSecretMap;
+    }
+	
+	@Override
+    public boolean supports(Class<?> authentication) {
+        return TaobaoAuthenticationToken.class.isAssignableFrom(authentication);
+    }
+
+	@Override
+	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+		this.applicationContext = applicationContext;
+	}
+}

src/java/com/mingidea/security/taobao/TaobaoAuthenticationToken.java

+package com.mingidea.security.taobao;
+
+import java.util.Collection;
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+
+@SuppressWarnings("serial")
+public class TaobaoAuthenticationToken extends AbstractAuthenticationToken {
+    private TaobaoCredentials credentials;
+    private Object principal;
+    
+    public TaobaoAuthenticationToken(TaobaoCredentials credentials) {
+        super(null);
+        this.credentials = credentials;
+        this.principal = credentials.getVisitorNick();
+        this.setAuthenticated(false);
+    }
+    
+    public TaobaoAuthenticationToken(Object principal, TaobaoCredentials credentials, Collection<? extends GrantedAuthority> authorities) {
+        super(authorities);    
+        this.principal = principal;
+        this.credentials = credentials;
+        this.setAuthenticated(true);
+    }
+
+    @Override
+    public Object getCredentials() {
+        return credentials;
+    }
+
+    @Override
+    public Object getPrincipal() {
+        return principal;
+    }
+}

src/java/com/mingidea/security/taobao/TaobaoCredentials.java

+package com.mingidea.security.taobao;
+
+import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.util.Assert;
+
+/**
+ * 表示淘宝登陆的回调参数。
+ * 参考:<a href="http://open.taobao.com/dev/index.php/%E7%94%A8%E6%88%B7%E9%AA%8C%E8%AF%81">TOP用户验证</a>
+ * @author Simon Leung
+ * @since 0.1
+ */
+public class TaobaoCredentials implements Serializable {
+    private static final long serialVersionUID = 5346807960373241353L;
+    
+    private String appKey;
+    private String session; 
+    private String parameters;
+    private String sign;
+    
+    //上下文参数,从top_parameters中解析所得
+    private long timestamp;
+    private String iframe;
+    private String visitorId;
+    private String visitorNick;
+    private String visitorRole;
+    
+    private TaobaoCredentials() {
+        //Can not be instantiated directly,use #build()
+    }
+    
+    /**
+     * Build a TaobaoCredentials instance by HTTP request.
+     * @param request
+     * @return
+     */
+    public static TaobaoCredentials build(HttpServletRequest request) {
+        Assert.notNull(request, "The request can't be null");
+        TaobaoCredentials credentials = new TaobaoCredentials();
+        credentials.appKey =  request.getParameter("top_appkey");
+        credentials.session =  request.getParameter("top_session");
+        //base64 encoded
+        String parameters = request.getParameter("top_parameters");
+        credentials.parameters =  parameters;
+        credentials.sign =  request.getParameter("top_sign");
+        
+        if(parameters != null) {
+            String charsetParameter = request.getParameter("encode");
+            String charset = charsetParameter == null ? "GBK" : charsetParameter;
+            String decodedParameters = null;
+            try {
+                decodedParameters = new String(Base64.decodeBase64(parameters), charset);
+            } catch (UnsupportedEncodingException e) {
+                //ignore
+            }
+            Map<String,String> parameterMap = parseParameters(decodedParameters);
+            credentials.timestamp = Long.parseLong(parameterMap.get("ts"));
+            credentials.iframe = parameterMap.get("iframe");
+            credentials.visitorId = parameterMap.get("visitor_id");
+            credentials.visitorNick = parameterMap.get("visitor_nick");
+            credentials.visitorRole = parameterMap.get("visitor_role");
+        }
+   
+        return credentials;
+    }
+
+    /**
+     * 将URL参数解析成Map
+     * 
+     * @param parameters like:key1=value1&key2=value2……
+     * @return key value map
+     */
+    private static Map<String,String> parseParameters(String parameters) {
+        Map<String,String> map = new HashMap<String,String>(5);
+        String[] pairs = StringUtils.split(parameters, "&");
+        for(String pair : pairs) {
+            String[] keyValue = StringUtils.split(pair, "=");
+            map.put(keyValue[0], keyValue[1]);
+        }
+        return map;
+    }
+    
+    public String getParameters() {
+		return parameters;
+	}
+
+	public String getSession() {
+        return session;
+    }
+
+    public String getAppKey() {
+        return appKey;
+    }
+
+    public String getSign() {
+        return sign;
+    }
+
+    public long getTimestamp() {
+        return timestamp;
+    }
+
+    public String getIframe() {
+        return iframe;
+    }
+
+    public String getVisitorId() {
+        return visitorId;
+    }
+
+    public String getVisitorNick() {
+        return visitorNick;
+    }
+
+    public String getVisitorRole() {
+        return visitorRole;
+    }
+}

src/java/com/mingidea/security/taobao/TaobaoUser.java

+package com.mingidea.security.taobao;
+
+public class TaobaoUser {
+	private String nick;
+	private String appKey;
+	private String session;
+
+	public TaobaoUser(String nick, String appKey, String session) {
+		this.nick = nick;
+		this.appKey = appKey;
+		this.session = session;
+	}
+	
+	public String getNick() {
+		return nick;
+	}
+
+	public void setNick(String nick) {
+		this.nick = nick;
+	}
+
+	public String getAppKey() {
+		return appKey;
+	}
+
+	public void setAppKey(String appKey) {
+		this.appKey = appKey;
+	}
+
+	public String getSession() {
+		return session;
+	}
+
+	public void setSession(String session) {
+		this.session = session;
+	}
+}

src/java/com/mingidea/security/taobao/TaobaoUserCreatedEvent.java

+package com.mingidea.security.taobao;
+
+import org.springframework.context.ApplicationEvent;
+
+public class TaobaoUserCreatedEvent extends ApplicationEvent {
+	private static final long serialVersionUID = 1L;
+	
+	private TaobaoUser taobaoUser;
+	
+	public TaobaoUserCreatedEvent(Object source, TaobaoUser taobaoUser) {
+		super(source);
+		this.taobaoUser = taobaoUser;
+	}
+
+	public TaobaoUser getTaobaoUser() {
+		return taobaoUser;
+	}
+}

src/java/com/mingidea/security/taobao/TaobaoUserUpdatedEvent.java

+package com.mingidea.security.taobao;
+
+import org.springframework.context.ApplicationEvent;
+
+public class TaobaoUserUpdatedEvent extends ApplicationEvent {
+	private static final long serialVersionUID = 1L;
+	private TaobaoUser taobaoUser;
+
+	public TaobaoUserUpdatedEvent(Object source, TaobaoUser taobaoUser) {
+		super(source);
+		this.taobaoUser = taobaoUser;
+	}
+
+	public TaobaoUser getTaobaoUser() {
+		return taobaoUser;
+	}
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.