一、背景介绍
1.自动化的配置工具autoconfig介绍
项目开发过程中,有些配置会随着运行环境的变化而各不相同。如jdbc驱动的配置,在开发环境可能链接到开发本地的数据库,测试环境则有一套测试专用的数据库环境,如果一个应用要部署到多个idc中,那这些配置又有可能各不相同。如果每次上线时候人工的修改一下配置,比较容易出错,而且随着环境的增多成本会线性地增长。
Autoconfig提供了一种动态替换配置信息的手段。并且Maven的强大插件机制,可以和autoconfig机制结合起来,发挥巨大的威力。pom.
<plugin> <groupId>com.alibaba.citrus.tool</groupId> <artifactId>autoconfig-maven-plugin</artifactId> <version>1.2</version> <configuration> <exploding>true</exploding> <includeDescriptorPatterns> <includeDescriptorPattern>autoconf/auto-config.</includeDescriptorPattern> <includeDescriptorPattern>autoconf/conf/auto-config.</includeDescriptorPattern> <includeDescriptorPattern>autoconf/conf/customize-autoconf/auto-config.</includeDescriptorPattern> </includeDescriptorPatterns> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>autoconfig</goal> </goals> </execution> </executions> </plugin>
pom.
Convention over Configuration(CoC),约定优于配置,上述配置文件中,充分体现了CoC原则。默认地插件会去扫描autoconf/auto-config.
package test;import java.io.InputStream;import java.util.Properties;public class Testconfig { /** * 从classpath中读取配置文件component-beans.properties,然后输出到控制台 */ public static void main(String[] args) throws Exception { InputStream is = Testconfig.class.getClassLoader().getResourceAsStream("component-beans.properties"); if (is == null) { System.err.println("Can not load config resource component-beans.properties in classpath"); } else { Properties prop = new Properties(); prop.load(is); is.close(); for (String key : prop.stringPropertyNames()) { String value = prop.getProperty(key); if (value != null) { System.out.printf("%s = %s %n", key, value); } } } }}
Testconfig.java
在Testconfig.java文件中,模拟实现了从classpath中读取配置文件component-beans.properties,接下来创建antoconfig的描述文件auto-config.component-beans.properties.vm配置文件对应的模板文件(属性名中的点在autoconfig执行的时候会被替换成下划线)。
通常将配置集中管理,使用中央配置仓库,将可变内容存储在数据库中(文本,DB都可以),然后使用模版引擎(velocity,jsp等)生成最终的配置文件,并且提供http或者其他类型的接口给使用方在应用启动前调用。
2.Apache加密工具类DigestUtils.md5Hex(String str)
MD5算法是单向不可逆的,目前只可以通过暴力破解来破解。如果应用中需要采用可逆的加密方法,可以采用DES,3DES,AES,RSA 等。MD5常用于用户的登陆密码加密,每次把用户输入的密码用MD5加密后跟数据库中存储的密码进行比较。可逆加密常用于前端与后端参数交互,后端解密参数,查询数据返回给前端。
MD5加密原理是散列算法,散列算法也称哈希算法。比如10除以3余数为一,4除以3余数也为一,但余数为一的就不知道这个数是哪个了。所以md5不能解密。
二、鉴权配置和初始化
首先,在component-beans.
<bean id="apiValve" class="com.alibaba.tboss.common.services.login.ApiAuthValve" init-method="initial"> <!-- 需要权限验证url --> <property name="includeStr"> <list> <value><![CDATA[^/tboss/web/api/.*\.json$]]></value> </list> </property> <!-- 需要验证模块中跳过的url--> <property name="excludeStr"> <list> </list> </property></bean>
apiValue定义
然后在pipeline-web.
<??><beans:beans> <services:pipeline ="http://www.alibaba.com/schema/services/pipeline/valves"> <!-- 初始化turbine rundata,并在pipelineContext中设置可能会用到的对象(如rundata、utils),以便valve取得。 --> <prepareForTurbine /> <!-- 设置日志系统的上下文,支持把当前请求的详情打印在日志中。 --> <setLoggingContext /> <!-- 分析URL,取得target。 --> <analyzeURL /> <valve class="com.alibaba.dragoon.patrol.webx3.DragoonStatValve" /> <!-- 配置ApiAuthValue API鉴权--> <valve class="com.alibaba.tboss.common.services.login.ApiAuthValve" /> <choose> <when> <!-- 判断当前的登录类型 --> <pl-conditions:condition class="com.alibaba.tboss.common.services.login.LoginTypeCondition" /> <valve class="com.alibaba.tboss.common.services.login.MobileAuthValve" /> </when> <otherwise> <valve class="com.alibaba.tboss.common.services.login.TbossAuthValve" /> </otherwise> </choose> <!-- 检查csrf token,防止csrf攻击和重复提交。假如request和session中的token不匹配,则出错,或显示expired页面。 --> <checkCsrfToken /> </services:pipeline></beans:beans>
pipeline-web.
接着实现了ApiAuthValve.java中的回调方法
package com.alibaba.tboss.common.services.login;import static com.alibaba.tboss.common.auth.costants.ApiConstants.API_AUTH_NAME;import static com.alibaba.tboss.common.auth.costants.ApiConstants.API_AUTH_SIGNATURE;import static com.alibaba.tboss.common.auth.costants.ApiConstants.API_AUTH_TIEMSTAMP;import java.util.ArrayList;import java.util.List;import java.util.regex.Pattern;import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import com.alibaba.citrus.extension.rpc.impl.DefaultResultGenerator;import com.alibaba.citrus.extension.rpc.validation.DefaultErrorContext;import com.alibaba.citrus.extension.rpc.validation.ErrorContext;import com.alibaba.citrus.extension.rpc.validation.ErrorItem;import com.alibaba.citrus.service.pipeline.PipelineContext;import com.alibaba.citrus.service.pipeline.support.AbstractValve;import com.alibaba.fastjson.JSON;import com.alibaba.tboss.common.auth.bo.ApiUserBo;public class ApiAuthValve extends AbstractValve { private static Logger logger = LoggerFactory.getLogger(ApiAuthValve.class); @Resource ApiUserBo apiUserBo; @Autowired private HttpServletRequest request; @Autowired private HttpServletResponse response; // 必须定义成 protected static ,否则注入不进来 protected static String[] includeStr; protected static List<Pattern> includes = new ArrayList<Pattern>(); protected static String[] excludeStr; protected static List<Pattern> excludes = new ArrayList<Pattern>(); public void initial() { if (includeStr != null) { for (int i = 0; i < includeStr.length; i++) { includes.add(Pattern.compile(includeStr[i].toLowerCase())); } } if (excludeStr != null) { for (int i = 0; i < excludeStr.length; i++) { excludes.add(Pattern.compile(excludeStr[i].toLowerCase())); } } } @Override public void invoke(PipelineContext pipelineContext) throws Exception { String uri = request.getRequestURI(); try { // isInControl=true表示请求链接需要鉴权 if (isInControl(uri)) { // 外部调用,必须添加的三个参数:apiName、timestamp、signature String apiName = request.getParameter(API_AUTH_NAME); String timestamp = request.getParameter(API_AUTH_TIEMSTAMP); String signature = request.getParameter(API_AUTH_SIGNATURE); // 没有从api_user表匹配权限 apiUserBo.checkAuthorization(apiName, timestamp, signature, uri); } } catch (Exception e) { logger.error(uri + "fail - " + e.getMessage(), e); // 折衷方案,依赖rpc extention ErrorContext errorContext = new DefaultErrorContext(); errorContext.addError(ErrorItem.create("rpc_500", "500", String.format("API auth fail : " + e.getMessage(), request.getRequestURI()))); Object result = new DefaultResultGenerator.GenericWebRPCResult("API auth fail", null, errorContext); response.getWriter().print(JSON.toJSONString(result)); return; } pipelineContext.invokeNext(); } private boolean isInControl(String uri) { boolean control = false; for (Pattern pattern : includes) { if (pattern.matcher(uri.toLowerCase()).matches()) { control = true; break; } } if (control) { for (Pattern pattern : excludes) { if (pattern.matcher(uri.toLowerCase()).matches()) { control = false; break; } } } return control; } public void setRequest(HttpServletRequest request) { this.request = request; } public void setResponse(HttpServletResponse response) { this.response = response; } public String[] getIncludeStr() { return includeStr; } public void setIncludeStr(String[] includeStr) { this.includeStr = includeStr; } public String[] getExcludeStr() { return excludeStr; } public void setExcludeStr(String[] excludeStr) { this.excludeStr = excludeStr; }}
ApiAuthValue.java
最后,在执行相应的业务逻辑。附鉴权方法的实现类如下:
import org.apache.commons.lang3.time.DateUtils;import com.alibaba.common.lang.MathUtil;import org.apache.commons.codec.digest.DigestUtils;public void checkAuthorization(String apiName, String timestamp, String signature, String uri) { if(StringUtils.isBlank(apiName) || StringUtils.isBlank(timestamp) || StringUtils.isBlank(signature)) { throw new ApiAuthException("apiName, timestamp, signature is missing"); } Date gmtRequest = null; try { gmtRequest = DateUtils.parseDate(timestamp, API_AUTH_TIMEFORMAT); } catch (ParseException e) { throw new ApiAuthException("Unsupported timestamp format, suggestting as " + API_AUTH_TIMEFORMAT); } if (MathUtil.abs(System.currentTimeMillis() - gmtRequest.getTime()) >= 15 * 60 * 1000) { throw new ApiAuthException("Request has been expired"); } ApiUser user = getApiUserByName(apiName); if(user == null) { throw new ApiAuthException("Api user is not exist"); } if(!isAuthorized(user, uri)) { throw new ApiAuthException(String.format("%s has no permission to access uri %s", apiName, uri)); } String realCode = decode(user.getCode()); String format = String.format("%s%s%s", apiName, timestamp, realCode); if(!signature.equals(DigestUtils.md5Hex(format))) { throw new ApiAuthException("Signature is not match"); } }
原标题:ApiAuthValue鉴权机制总结
关键词: