Java

转:为spring mvc加上csrf防范的简单方案

官方文档建议你引入spring security,然后用一行简单的xml配置就可以搞定csrf防范。 但是引入spring security意味着你必须使用它的一套authentication & authorization机制,而且这玩意儿也不轻量。 网上搜了下,下面这个方案简单又可靠,你抄一些它的代码即可: http://blog.eyallupu.com/2012/04/csrf-defense-in-spring-mvc-31.html ======= 以上只对普通form提交方式有效 对于ajax方式,可以在页面任何一处写一个<form:form/>,就可以在页面源码中生成这样一个token; ajax在提交时,把这个token作为post的内容提交就可以了

代码片断:远程接口返回数据中的属性过滤

从网络流量考虑,我们的远程接口有时并不需要返回所有数据。为此,可以通过反射去掉不要的属性。 /** * 过滤属性,省流量 * * * @param bean * @param filter */ public static void filterProps(Object bean, ResultPropsFilter filter) { if (filter == null) { return; } PropertyDescriptor[] pdArray = PropertyUtils .getPropertyDescriptors(bean); if (pdArray == null) { return; } for (PropertyDescriptor pd : pdArray) { String propName = pd.getName(); if(propName.equals("class")){ continue; } Class<?> type …

代码片断:远程接口返回数据中的属性过滤 Read More »

基于比较的分页机制的注意事项

分页机制有两种: 一种是根据偏移量(或页码)+页大小获取数据,另一种是根据上一次看过的最后一条记录+页大小获取本页记录。 后一种机制常见于weibo/twitter这种feed流页面:记住当前页最后一条feed的发表时间,然后找出比这个时间更早的feed.   这种情况下一般不需要知道feed总数。 姑且把这种机制称为“基于比较的分页”。 使用这种机制时,需要注意的地方有: 1. 用作比较的维度应该是唯一的,在做大小比较时才不会遗漏数据。如果按ID来比较,则比id=1更大的记录是(2,3,…),不会遗漏;  如果按发表时间来比较,则比100毫秒更大的记录是(101, 102…), 而同在第100毫秒发表的其他记录就会被遗漏掉。 2. 除了往下翻页,还可能要往上翻页; 系统应该同时支持。 3. “首页”应该传什么值作为比较基准? 如果id为Long型,应该传-1还是传一个Long.maxValue()? 这种地方最容易埋坑,引发程序错误和沟通困难。 最好的办法是再另传一个参数:firstPage = true, 简单明了。 4. 服务端回传记录时,应该同时回传本页记录第一条和最后一条的比较值,比如第一个feed的id和最后一个 feed的id; 客户端/浏览器 再根据这两个记录决定上一页和下一页的URL。你会说不传也可以,客户端自己从feed列表中找出这两个id; 然而用于比较分页的维度未必是feed的属性,从feed列表中是找不出来的,比如分页查询“我评论过的feed”时,要通过评论ID来做比较,评论ID显然并不是feed的属性。 5. 第4点帮助你得出一个结论:分页参数可以跟业务无关,客户端不必知道分页维度的业务意义; 同理,也不必知道下一页的记录是比某个属性更“大”,还是更“小”,客户端只需要传“下一页”,或是“上一页”,让服务端自己决定取更大或是更小的记录。 6. 有了这个结论,我们可以做一个通用的、适用于任何业务分页参数类。示范代码: 分页参数类: public class PageByCompareParam { * 服务端将跟这个值比较。日期时间类型请传毫秒数时间戳,数字类型将转成字符串后再传过来 */ private String value; /** * 页大小 */ private int pageSize; /** * true = …

基于比较的分页机制的注意事项 Read More »

Jdbc代码片断:使sql in子句中的记录数恒定

private List<Long> toNormalizedList(List<Long> idList) { List<Long> newList = new ArrayList<Long>(); newList.addAll(idList); for (int i = idList.size() + 1; i <= 100; i++) { // 填满100个,使得每个prepared // statement都有100参数,以充分利用sql缓存 newList.add(idList.get(0)); } return newList; } 相应的ibatis sql map: <select id="batchGet" parameterClass="list" resultClass="java.lang.Long"> select distinct * from some_table where id in <iterate open="(" close=")" conjunction=","> #[]# </iterate> </select>

代码片断:在m位数之前加0,凑足n位

代码片断:在m位数之间加0,凑足n位. 这主要用于使数字按字符串排序的顺序跟直接按数字排序的顺序相同 比如把 8,9,10,11 变成 08, 09, 10, 11后,按字符串序排序仍与数字大小序相同 /** * 1 => 0001 * * @param positiveNumber * @param maxValue * @return */ private static String prependZeroForNumber(int positiveNumber, int maxValue) { int digitLength = String.valueOf(maxValue).length(); String numStr = String.valueOf(positiveNumber); StringBuffer pre = new StringBuffer(); for (int i = 0; i < digitLength – numStr.length(); …

代码片断:在m位数之前加0,凑足n位 Read More »

代码片断: sql + java实现数据库某字段的原子递增

“update t set v = v + 1 where id = 1”  这个语句可以用于数据库某字段值的递增,但在并发情况下,它很容易造成”丢失修改“。要解决这个问题,一般要用乐观锁。 下面是我最近在项目中使用的做法: –使用cas风格 update t set v = #newValue# where id = 1 and v = #currentValue# //乐观锁+重试机制,如果重试了一定次数还未成功,则抛出 IllegalStateException public void increaseValue(long id) throws IllegalStateException { //如果cas失败,则继续执行,直到100遍 for (int i = 0; i < 100; i++) { int currentValue = getValueById(id, topicName); if …

代码片断: sql + java实现数据库某字段的原子递增 Read More »

Aspirin: 不用指定任何smtp服务器,用JAVA发email

https://github.com/masukomi/aspirin 可以帮你发出email,  不用你显式指定一个smtp服务器。 API也非常简单。 看了一下它的源码,原来它的原理是: 当你指定收件人为xxx@yyy.com时,它会试着去DNS上找一下yyy.com对应的MX记录,作为临时的smtp server:   MX = a type of resource record in the Domain Name System that specifies a mail server responsible for accepting email messages on behalf of a recipient’s domain

Netty: 几个关键对象生命周期之间的关联

pipeline, handler, channelHandlerContext这样对象倒底是channel级的,还是event级的,又或者是系统级的? 或者这样问,一个连接中只有一个相应的实例,抑或每来一个消息都会生成一个相应的实例,又或者整个系统共享一个相应的实例? 一般来说,一个pipeline实例只属于一个channel,不会跨channel共享。因为你的pipeline factory一般会这样写: ChannelPipeline pipeline = Channels.pipeline(); //为channel生成pipeline时总是new一个 当然,如果你有特殊需求,你也可以改变写法,让pipeline在channel间共享。下面假设你没有特殊需求,一个pipeline实例只需于一个channel. 一个channel只有一个pipeline. Channel建立时,会通过pipeline factory新生成相应的pipeline对象,ChannelPipeline pipeline = Channels.pipeline(); 后续的读写中,直接使用这个pipeline对象,不再创建新的pipeline. 由于pipeline和handler的结构关系,所以对于单种handler,一个pipeline实例中只有一个handler实例,除非你的pipeline中有重复的handler. 一个handler的实例可以只属于一个pipeline实例, pipeline.addLast("decoder", new HttpRequestDecoder()); 也可以声明为单例,在pipeline实例间共享,也就是在多个channel间共享,这时你要注意线程安全的问题。 pipeline.addLast("decoder", httpRequestDecoder()); pipeline实例中每新增一次handler(即使是同一个实例), 都会生成一个相应的ChannelHandlerContext实例。这里可以推出两个结论:   1. pipeline中如果有两个handler,  那么每个handler各有各的ChannelHandlerContext对象,互不相干。   2. 对于一个channel来说,只会生成一次pipeline,所以对于单种handler,一个pipeline实例中只有一个相应的ChannelHandlerContext实例。也就是说,对于单种handler, 相应的channelHandlerContext是channel级的,而不是event级的。