1

问题描述

于学期末有一个大实验,要求使用Java相关技术,计划采用AngularJSSpringBoot开发。

为了提高开发效率,特地在后台启用Spring Data REST,少写了很多增删改查,不得不感叹SpringBoot是真的厉害,只在配置文件中加入以下的配置,然后就在配置的url上生成了相应实体的增删改查、分页排序接口。

spring:
  data:
    rest:
      base-path: /data

除了基础的代码之外,登录也是一大问题,前后台分离如何进行用户认证呢?

用户认证

学习了潘老师的教程之后,对Springsession管理也是有了一定了解。

HTTP

HTTP is stateless: But while the core of HTTP itself is stateless, HTTP cookies allow the use of stateful sessions. Using header extensibility, HTTP Cookies are added to the workflow, allowing session creation on each HTTP request to share the same context, or the same state.

HTTP是无状态的,虽然HTTP自身是无状态的,但是cookie允许有状态session的使用。通过使用Header的扩展,cookie被添加到工作流中,这些cookie允许在每一个HTTP请求创建会话,去共享上下文,或共享状态。

浏览器有cookie,服务器有session

Session

SpringBoot中,我们使用依赖注入自动装配HttpSession,用于管理session

clipboard.png

但是,Spring的所有组件,包括ControllerServiceComponent默认都是单例模式,那当多用户请求时,是如何保证这个session,就是当前用户的session呢?

Servlet

知其然,知其所以然。

框架只是我们提升开发效率的一种方式,没了框架,我们一样可以写,只是会造重复的轮子。

Java进入web领域时,所有的开发都是基于Servlet接口的,而我们常说的Tomcat是一个Servlet容器。

因为通过HTTP协议进行数据通信,所以常用的是HttpServlet抽象类。

clipboard.png

在所有的会话跟踪技术中,HttpSession对象是最强大和最通用的。

HttpSession对象在用户第一次访问网站的时候自动被创建,可以通过调用HttpServletRequestgetSession方法获取该对象。

HttpSession getSession();

可以通过HttpSessionsetAttribute方法将值放入HttpSession,该值存储在内存中,所以不要往HttpSession中放入太多数据。

void setAttribute(String name, Object value);

login

所以,最原始的写法就像下面这样,从HttpServletRequest中获取HttpSession,并设置属性。

request.getSession.setAttribute(CommonService.UserId, user.getId());

Apache Tomcat的官方文档中找到了下面一句话。

public interface HttpSession

Provides a way to identify a user across more than one page request or visit to a Web site and to store information about that user.

公共接口 HttpSession

提供一种认证跨页面或浏览Web站点的用户并存储用户相关信息的方式。

所以,我猜想,从cookiesession的绑定,应该是Tomcat为我们处理的。

如果请求中没有cookie,在getSession时创建一个新的session给我。

如果请求中有cookie,在getSession时查询已有的session,然后把当前cookie对应的session给我。

SpringMVC

SpringMVC中,请求的分发是通过org.springframework.web.servlet.DispatcherServlet完成的。

[dɪˈspætʃə(r)] ['sɜ:vlet]
Central dispatcher for HTTP request handlers/controllers, e.g. for web UI controllers or HTTP-based remote service exporters. Dispatches to registered handlers for processing a web request, providing convenient mapping and exception handling facilities.

HTTP请求的前端控制器,为了web ui控制器或者基于HTTP的其他服务,它为一个web请求注册handler,提供了方便的映射与异常处理工具。

因为控制器的方法是被DispatcherServlet所调度的,DispatcherServlet持有请求与响应对象。

所以可以直接在控制器中注入相关对象。

Spring 参考文档

可以直接注入HttpServletRequestHttpSession等对象。

类似下面:

@PostMapping("/test")
public void test(HttpServletRequest request, HttpSession session) {
}

当然,因为request中就有session,所以也可以在request中获取session

以上方法肯定没问题,直接在参数列表中注入,肯定线程安全。

如果改成如下写法,它还是线程安全的吗?

@Autowired
private HttpServletRequest request;
@Autowired
private HttpSession session;

@PostMapping("/test")
public void test() {
}

Spring 中获取 request 的几种方法,及其线程安全性分析

经测试,是的。Controller是单例,但是Controller是线程安全的。

因为Spring容器在启动的时候,创建Controller实例,但是HttpServletRequestHttpSession注入的是代理对象,每次请求通过代理对象获取当前请求的requestsession

所以单例Controller能保证不同请求有不同的session,并且是线程安全的。

官方是更加推荐使用@Autowired注入HttpSession,因为这样注入的方式不局限于控制器内,如果Service中也想用当前的session,也能注入进来。

附加

记得之前对实体中某个属性设置not null,发现有@Column(nullable = false)@NotNull两种方法。

今天在看《SpringMVC初学指南时》明白了第二种写法。

第一种是Hibernate中的,是在插入数据库时进行验证。

第二种是SpringMVC支持的JSR(Java规范请求,Java Specification Requests)中的,用于对控制器中接收的对象进行验证。

所以实际符合需求的是@Column(nullable = false)

@PostMapping("/test")
public void test(@Valid @RequestBody Teacher teacher) {
}

JSR规范约束表

属性 描述
@AssertFalse boolean属性必须为false
@AssertTrue boolean属性必须为true
@DecimalMax 该属性值必须小于等于指定的小数
@DecimalMin 该属性值必须大于等于指定的小数
@Digits 应用于BigDecimal,约束最大整数与最大小数。
@Future 必须是未来的一个日期
@Past 必须是过去的一个日期
@Max 最大值
@Min 最小值
@NotNull 不为Null
@Null 必须为Null
@Pattern 必须匹配该正则表达式
@Size 必须在指定范围内

总结

书籍,举世之宝!
感谢河北工业大学的图书馆,有各种于开发有益的书籍,受益良多。
Facebook的全栈工程师,在遇到一个问题时,在亚马逊上买一本相关书籍,通读一夜,然后解决问题。一起加油!

张喜硕
2.1k 声望423 粉丝

浅梦辄止,书墨未浓。


« 上一篇
对象克隆问题
下一篇 »
Entity 监听器