Velocity模板语法(类似Java语法)

$!{ 变量/表达式 }

## 注释 ## #* 多行注释 *#

for

#foreach ($color in $colors)

Color$!{foreach.count}/${foreach.index}:$!{color}

#end

默认目录:templates

文件:xxx.vm

属性访问

$!{user.name}

$!{user.getName()}

HttpServletResponse
response.addCookie(new Cookie(key, value));
response.addHeader(key, value);

HttpServletRequest
request.getHeaderNames();
request.getMethod()
request.getPathInfo()
request.getQueryString()

@RequestMapping(value = {"/response"})
    @ResponseBody
    public String response(@CookieValue(value = "nowcoderid", defaultValue = "a") String nowcoderId,
                           @RequestParam(value = "key", defaultValue = "key") String key,
                           @RequestParam(value = "value", defaultValue = "value") String value,
                           HttpServletResponse response) {
        response.addCookie(new Cookie(key, value));
        response.addHeader(key, value);
        return "NowCoderId From Cookie:" + nowcoderId;
    }

重定向

301:永久转移

302:临时转移

@RequestMapping("/redirect/{code}")
  public String redirect(@PathVariable("code") int code,
                           HttpSession session) {
        /*
        RedirectView red = new RedirectView("/", true);
        if (code == 301) {
            red.setStatusCode(HttpStatus.MOVED_PERMANENTLY);
        }
        return red;*/
        session.setAttribute("msg", "Jump from redirect.");
        return "redirect:/";
    }

redirect前缀,跳到首页,默认是302跳转。
从一个页面跳到另一个页面,所有的访问都是同一个HttpSession,可以在redirect中添加session的一些特性,返回到首页的时候,把session的信息读取出来,显示在首页。用户体验较好。
301是永久迁移,如果是301,会把信息存入浏览器,下次浏览器访问网址,会直接定位到另一个地方。
301是临时迁移。

AOP
面向切面,所有业务都要处理的业务

@Aspect
@Component
public class LogAspect {
    private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);

    @Before("execution(* com.nowcoder.controller.IndexController.*(..))")
    public void before(JoinPoint joinPoint) {
        StringBuffer sb = new StringBuffer();
        for (Object arg : joinPoint.getArgs()) {
            sb.append("arg:" + arg.toString());
        }
        logger.info("before method:" + sb.toString());
    }

    @After("execution(* com.nowcoder.controller.*Controller.*(..))")
    public void after(JoinPoint joinPoint) {
        logger.info("after method:");
    }
}

JoinPoint是个包装类,相当于面向切面的交汇点,通过getArgs方法可以获得所有的进入controller的输入参数

ViewObject:方便传递任何数据到Velocity
DateTool:velocity自带工具类导入

public class ViewObject {

    private Map<String, Object> objs = new HashMap<String, Object>();

    public void set(String key, Object value) {
        objs.put(key, value);
    }

    public Object get(String key) {
        return objs.get(key);
    }
}

可以知道用户是这个用户。
注册成功后会进行自动登陆,对于登陆,在登陆操作中,在service层,进行逻辑判断,对上返回状态回到controller,对下dao去和数据库交互。在service登陆代码中,服务器会生成一个string类型的ticket,存入cookie中,key值是ticket,value值是ticket的值。通过response下发到浏览器。
下次在已经登陆的用户,进行其他点击后。在进入controller前,调用preHandle方法处理,它可以检查客户端提交的cookie中是否有服务器之前下发的ticket,如果有证明这个请求是已经登陆的用户了。把登陆的用户放到线程本地变量。在此时才进入controller,可以拿到具体的用户HostHolder类,这是线程本地变量,可以根据登陆的用户进行个性化渲染,比如关注用户的动态,个人收藏等

新建数据表login_ticket用来存储ticket字段。该字段在用户登录成功时被生成并存入数据库,并被设置为cookie,
下次用户登录时会带上这个ticket,ticket是随机的uuid,有过期时间以及有效状态。

使用拦截器interceptor来拦截所有用户请求,判断请求中是否有有有效的ticket,如果有的话则将用户信息写入Threadlocal。
所有线程的threadlocal都被存在一个叫做hostholder的实例中,根据该实例就可以在全局任意位置获取用户的信息。

该ticket的功能类似session,也是通过cookie写回浏览器,浏览器请求时再通过cookie传递,区别是该字段是存在数据库中的,并且可以用于移动端。

如下有个PassportInterceptor拦截器,它对于所有页面都进行处理

@Component
public class PassportInterceptor implements HandlerInterceptor{

    @Autowired
    private UserDAO userDAO;

    @Autowired
    private LoginTicketDAO loginTicketDAO;

    @Autowired
    private HostHolder hostHolder;

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        //处理用户信息,判断是否有ticket,一个用户一个ticket,但是有时限
        String ticket = null;
        if (httpServletRequest.getCookies() != null) {
            for (Cookie cookie : httpServletRequest.getCookies()) {
                if (cookie.getName().equals("ticket")) {
                    ticket = cookie.getValue();
                    break;
                }
            }
            //判断ticket是否过期和无效
            if (ticket != null) {
                LoginTicket loginTicket = loginTicketDAO.selectByTicket(ticket);
                if (loginTicket == null || loginTicket.getExpired().before(new Date()) || loginTicket.getStatus() != 0) {
                    return true;
                } else {
                    User user = userDAO.selectById(loginTicket.getUserId());
                    //将用户信息报错到当前请求线程中
                    hostHolder.setUsers(user);
                    return true;
                }
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        //渲染之前提供的后处理方法,可以添加模型数据,自动传给前端
        if (modelAndView != null && hostHolder.getUser() != null) {
            modelAndView.addObject(hostHolder.getUser());
            hostHolder.clear();
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        hostHolder.clear();
    }
}

拦截器1放行,拦截器2 preHandle才会执行。

拦截器2 preHandle不放行,拦截器2 postHandle和afterCompletion不会执行。

只要有一个拦截器不放行,postHandle不会执行

头条资讯项目-LMLPHP

用户数据安全性:
https可以防止运行商加塞广告,
公钥加密私钥解密,
用户密码salt防止破解
token有效期
单一平台的单点登陆,登陆IP异常检验
用户状态的权限判断
添加验证码机制,防止爆破和批量注册。
手机验证码,一瞬间一万个请求,0~9999都发到服务器上,总有一个对的上的。如果一个验证码失效,重新下发token

Spring Boot Dev Tools
动态加载更新的class
编译加载修改的静态文件

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>

上传图片
一、post方法上传图片到服务器本地,返回UUID的string字符串,形成URl,浏览器可以输入127.0.0.1 + UUID生成的string 组成URL,后台会传回给浏览器response,带有图片的二进制流。浏览器的response中就可以看到接收的图片二进制流,显示出来。fiddler可以通过代理的方式,把网络的底层全部拦截下来
上传到七牛云,做云存储

官方文档:

https://developer.qiniu.com/kodo/sdk/1239/java#upload

<dependency>
			<groupId>com.qiniu</groupId>
			<artifactId>qiniu-java-sdk</artifactId>
			<version>7.1.1</version>
		</dependency>
@Service
public class QiniuService {

    private static final Logger logger = LoggerFactory.getLogger(QiniuService.class);
    //设置好账号的ACCESS_KEY和SECRET_KEY
    String ACCESS_KEY = "DEYWFFCReQqVMYESzB4qo9WYwYtxmVf-8DHU34Jr";
    String SECRET_KEY = "5bFWFSYb9Gyx5v-5wYnket58cashXLw2-aZ6D3g2";
    //要上传的空间
    String bucketname = "toutiao";

    //密钥配置
    Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY);
    //创建上传对象
    UploadManager uploadManager = new UploadManager();

    private static String QINIU_IMAGE_DOMAIN = "http://p8aa1ssg4.bkt.clouddn.com/";

    //简单上传,使用默认策略,只需要设置上传的空间名就可以了
    public String getUpToken() {
        return auth.uploadToken(bucketname);
    }

    public String saveImage(MultipartFile file) throws IOException {
        try {
            int dotPos = file.getOriginalFilename().lastIndexOf(".");
            if (dotPos < 0) {
                return null;
            }
            String fileExt = file.getOriginalFilename().substring(dotPos + 1).toLowerCase();
            if (!ToutiaoUtil.isFileAllowed(fileExt)) {
                return null;
            }

            String fileName = UUID.randomUUID().toString().replaceAll("-", "") + "." + fileExt;
            //调用put方法上传
            Response res = uploadManager.put(file.getBytes(), fileName, getUpToken());
            //打印返回的信息
            if (res.isOK() && res.isJson()) {
                return QINIU_IMAGE_DOMAIN + JSONObject.parseObject(res.bodyString()).get("key");
            } else {
                logger.error("七牛异常:" + res.bodyString());
                return null;
            }
        } catch (QiniuException e) {
            // 请求失败时打印的异常的信息
            logger.error("七牛异常:" + e.getMessage());
            return null;
        }
    }
}

云可以做实时缩图和实时切图

云实时缩图

阿里云
http://images.nowcoder.com/images/20150205/60_1423125965233_60_1423125960758_%E7
%AE%A1%E7%90%86%E5%91%98%E5%A4%B4%E5%83%8F.png@0e_100w_100h_0c_1i_1o_9
0Q_1x.png

七牛云
http://7xsetu.com1.z0.glb.clouddn.com/300_300.png?imageView2/1/w/100/h/100/
http://developer.qiniu.com/code/v6/api/kodo-api/image/imageview2.html

04-20 22:20