目录
知识点:spring security 、oauth2、jpa 、thymeleaf 以及 spring boot 框架的知识
各个知识点 起到的作用:
机制
依赖
流程:
client(客户端)
Auth-server(认证服务器包括资源服务器)
jpa部分
spring security 部分
还有很重要的一部分 Resource Server
thymeleaf部分
知识点:spring security 、oauth2、jpa 、thymeleaf 以及 spring boot 框架的知识
希望初次接触的同学去官网 搜索这些东西 看看一些简单的demo spring boot guides
先放 代码 auth-server: https://github.com/weitiancai/auth-server.git
client: https://github.com/weitiancai/client.git 说明:需要先在 mysql 里创建一个 名为 autologin2 的数据库
各位下载的时候顺手给个小星星好不。。
概念:单点登录 演示效果: 我b站第二个视屏。。 可以看到认证去了7778端口,完成后返回了7777端口。
各个知识点 起到的作用:
spring security : 对网页访问权限进行控制,你无权,你无法访问。
oauth2: 授权的,这个“2” 的意思是,授权的是第二方,如,我们可以用qq账号登录很多网站,这个qq的授权服务相对于你要登录的网站就是第二方。
直白的说: oauth2 给用户授权,spring security 根据你的权,开放给你页面访问,这个就是单点登录的核心所在了
既然有第二方授权,那么后台的服务肯定不止一个,正如我上面提供的两个git 项目地址一样,我把整套机制汇总到了两个服务里面——1、客户端 2、认证服务器(包括资源服务器)
thymeleaf : 前端 框架 、和spring boot 比较契合好用,不用前后端分离开发
jpa : 和 mysql 数据交互用,可以根据代码自动生成mysql 的表 (用过mybatis-plus 的同学会发现两个东西的过程刚好相反)
机制
其实sso 完整的机制就是 客户端、认证服务器、资源服务器 这三块 放张图来证明我的观点, oauth2 框架 原理
上面地址 是 来讲述 oauth2 框架原理的,我没仔细看(那也得有时间去看。。),我之前大把精力用来实现功能上了,而且这种封装好的,除非你想改源代码,知道不知道原理,实现起来 一样的,但是大致过程肯定是要清楚地。
和shiro 框架采用token 认证不一样, oauth 采用了从cookie 里面获取token的方法,只需要使用一次,并且一旦确定你的登录合法,之后开放给你的网址权限就全部开放了,不会像shiro 一样每次访问一个接口都要带上token。(我有什么讲错的可以评论里跟我讲) 我把 resource server 和 authorization server 合为一个服务,这是通用的做法。
依赖
client:
<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>
auth-server
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.security.oauth</groupId><artifactId>spring-security-oauth2</artifactId><version>2.3.3.RELEASE</version></dependency></dependencies>
突然发现要讲的东西好多,我还是从功能点来一个个讲吧。
流程:
1、用户访问客户端网址 --》2、用户没有登录( 即网址没有被授权) --》3、跳转到auth-server (认证服务器,下面只写英文)--》4、用户注册并登录账号密码(并成功后)--》5、跳转到原本想访问的那个网站(此时有了访问该网站的权限)
如果用户关闭了浏览器,(并没有清理cookie)则当用户第二次访问想要访问的网站时,可以直接进入(cookie 里保存的token未过期的情况)
如果用户手动退出,则需要再次在auth-server 登录账号密码访问 网站。(其实就是主动让cookie里的token过期,并跳转到登录界面而已)
client(客户端)
给网站授予访问权限是 spring-security 干的事情,在 client 项目里的 OauthConfig 类 ,继承 WebSecurityConfigurerAdapter
重写(@Override) 的 config (HttpSecurity http) 函数,就是自定义那些url 可以访问,哪些 是要有权限才行
@EnableOAuth2Sso
@Configuration
public class OauthConfig extends WebSecurityConfigurerAdapter{@Value("${auth-server}/exit")private String logoutUrl;@Overrideprotected void configure(HttpSecurity http) throws Exception {http.antMatcher("/**").authorizeRequests().antMatchers("/", "/login**").permitAll().anyRequest().authenticated();http.logout().logoutSuccessUrl(logoutUrl).and().authorizeRequests().anyRequest().authenticated();}
}
一行行解释,antMatcher 指 ant 路径匹配法 知识点 表明所有匹配到这类 路径的,我要干点啥,
authorizeReuqests() 干啥呢,看名字—— 需要权限,结合上面 那就是 所有 ‘/’ 开头的都要要权限,所以基本都要权限了
而下面 几行
.antMatchers("/", "/login**") 对于这两个路径,
.permitAll() 所有用户都可以访问
.anyRequest() 任何请求
.authenticated(); 都认证过了 (这个比较难理解,这么说吧,为了让这些url 不用授权即可访问,设计者就允许程序编写者使用authenticted方法,将这些url先自定义为已经过认证的网址,是不是很妙)
也就是说,开放 / 和 /login 这两个为可访问的,所以我们访问 / 地址的时候,redirect (重定向) 到了 index.html 页面 代码:
@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {configurer.enable();}@Overridepublic void addViewControllers(ViewControllerRegistry registry) {super.addViewControllers(registry);registry.addViewController("/").setViewName("forward:/index");registry.addViewController("/index");registry.addViewController("/secure");}@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");}@Beanpublic RequestContextListener requestContextListener() {return new RequestContextListener();}@Beanpublic static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {return new PropertySourcesPlaceholderConfigurer();}
}
addViewController 里面 就 注册了 / /index /secure 这三个view because ‘/ ’ direct to ‘/index ’ and the '/' is permited to access ,so you can visit the static resource —— index.html but you can't visit secure.html (英语真好~) 下面是资源文件夹内容。 我们要实现的就是在 index.html 点击跳转按钮then visit the secure.html
正如上面所说,访问不了 secure.html ,你需权限,你还没登录,这怎么办呢,没事,这个跳转你只需要配置,我们加的Oauth2依赖就给你设计好了怎么跳转等细节问题., 其实仔细看代码的话,可以看到 client 的 OauthConfig 类 有一个 注解
按Ctrl+左键
可以看到包含了
表明这个是 Oauth2 的客户端
yml 文件配置如下:
server:port: 7778context-path: /uisession:cookie:name: UISESSIONauth-server: http://localhost:7777/authsecurity:basic:enabled: falseoauth2:client:clientId: ClientId #客户端 idclientSecret: secret # 客户端的 密码accessTokenUri: ${auth-server}/oauth/tokenuserAuthorizationUri: ${auth-server}/oauth/authorize
# scope: read, writeauthorized-grant-types: authorization_coderesource:userInfoUri: ${auth-server}/hello/principalspring:thymeleaf:cache: false
debug: true
本机的server.port context-path session.cookie.name
auth-server : 即为 auth-server(认证服务器) 配置地址 端口 7777
Oauth 相关 的 clientId clientSecret 是为了让auth-server 能够知道到你是自己人。。 auth-server 内部也要有这两个
accessTokenUri: ${auth-server}/oauth/token
userAuthorizationUri: ${auth-server}/oauth/authorize
authorized-grant-types: authorization_code
这个是固定的,除非你自定义接口地址,作用就是 上面图中的 token cookie 拿来拿去的地址,很复杂的,详细请看上面链接。
resource:userInfoUri: ${auth-server}/hello/principal
这个配置及其重要,这个resource 即 资源服务,这个资源其实是包括登录的用户的信息的,it's very very very important!
虽然到时候auth-server 里写的接口很简单,但那是框架设计的好。
好了,@EnableOauth2Sso + application.yml 配置 ,登录的认证(输账号密码) 就会到auth-server :http://localhost:7777/auth 这里来了,没错,就是这么简单,接下来将auth-server 了
Auth-server(认证服务器包括资源服务器)
在访问client web网页的时候,如果是需要授权,则在“检查”里的状态值为“302” 如我要点击client 页面的a标签后,本应跳转到相应的7778 端口的secure.html 页面,可以发现去了auth-server (7777端口)的登录界面,检查(F12)看一下发生了什么
三个302 的状态之后,到了一个200 状态的login 页面,302什么意思,简单的概括这个过程,就是服务器端发现你没有认证,那么就会给你经过一系列重定向网址到登录界面,如果说你已经登录过了,我们可以看看差别(还是那个链接点击)
可以看到直接就进入了,其实因为在客户顿和服务器都保存了必要的cookie 和 session 数据的缘故,我们在推出,然后重新登录,可以看到下面network 里面 经历了 一个authorize?xxx (红框) 和 login?xxx(蓝框) 这两个重定向网址的过程,我们可以猜测就是这两个过程,分别在服务端和客户端记录了必要的信息,这是下次登录直接可以进入的前提,除非这些信息过时了。
好了,说一说,auth-server 的构造,原理
jpa部分
这个乏善可陈吧,其实定义好Entity类,一些注解我就不讲了,主要是User类中 @ManyToMany(多对多关系)的注解需要一定得数据库知识和对jpa 的理解。以为实体只有两个 —— user、role 其实还需要将这两张表关联起来,就用到了第三张表,其实就是我们学习数据库课上ER图里面的 “关系”,ER图里面不仅实体可以生成一张表,关系也会成为一张表(我数据库的老师看到会感动的要死,我真的还记得~)
@ManyToMany(cascade = CascadeType.ALL)@JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))private Set<Role> roles;
所以这个注解、属性的意思呢,就是我需要有这个 “user_role” 的关系(表),知识点来了,jpa根据 这个 名称就知道,这是那两张表的关系了!!!,你如果不信,可以试试,我没试过,我在看一篇外国文章的时候偶然发现的,一下子让我懂了这个注解的工作原理。 知道是那两张表的关系,我们就需要设定 这个 关系(表) 里的字段名称为什么,如上,是user_id, role_id 可是这两个字段的类型分别是什么呢,没错,就是 user 、role 表的主键啊,进而联想到外键的定义,user_id,role_id 不就是两个外键吗,而且,其实这两个字段还是user_role 表的两个主键(主键可以是一组o)
为了不下载代码的小伙伴也能看的更清楚,我还是放一下这两个Entity 的代码吧:
role:
package com.gpch.login.model;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import javax.persistence.*;@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "role")
public class Role {@Id@GeneratedValue(strategy = GenerationType.AUTO)@Column(name = "role_id")private int id;@Column(name = "role")private String role;@Overridepublic String toString() {return "Role{" +"id=" + id +", role='" + role + '\'' +'}';}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getRole() {return role;}public void setRole(String role) {this.role = role;}
}
user:
package com.gpch.login.model;//import javafx.scene.control.PopupControlBuilder;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import java.util.Set;@Data
@Builder
@AllArgsConstructor
@Entity
@Table(name = "user")
public class User {@Id@GeneratedValue(strategy = GenerationType.AUTO)@Column(name = "user_id")private Integer id;@Column(name = "email")@Email(message = "*Please provide a valid Email")@NotEmpty(message = "*Please provide an email")private String email;@Column(name = "password")@Length(min = 5, message = "*Your password must have at least 5 characters")@NotEmpty(message = "*Please provide your password")private String password;@Column(name = "name")@NotEmpty(message = "*Please provide your name")private String name;@Column(name = "last_name")@NotEmpty(message = "*Please provide your last name")private String lastName;@Column(name = "active")private int active;@ManyToMany(cascade = CascadeType.ALL)@JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))private Set<Role> roles;public User() {}public User(User users) {this.active = users.active;this.email = users.email;this.id = users.id;this.lastName = users.lastName;this.name = users.name;this.password = users.password;this.roles = users.roles;}// public static <B extends PopupControlBuilder<B>> PopupControlBuilder<B> builder() {
// return null;
// }@Overridepublic String toString() {return "User{" +"id=" + id +", email='" + email + '\'' +", password='" + password + '\'' +", name='" + name + '\'' +", lastName='" + lastName + '\'' +", active=" + active +", roles=" + roles +'}';}public void setId(Integer id) {this.id = id;}public void setIdNull(){this.id = null;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName = lastName;}public int getActive() {return active;}public void setActive(int active) {this.active = active;}public Set<Role> getRoles() {return roles;}public void setRoles(Set<Role> roles) {this.roles = roles;}
}
然后 分别创建两个repository 接口就可以使用jpa 提供的数据库crud方法了
在application.yml 文件里 添加配置
# ==============================================================
# = Hibernate ddl auto (create, create-drop, update)
# ==============================================================
spring.jpa.hibernate.ddl-auto = update
就可以在spring boot 运行的时候自动生成所需的mysql 的表了,没错,按照Entity自动生成对应table。理论上连数据库的名字也是可以自动生成的,不过我没找到响应的方法,所以还是需要手动先创建一个数据库,如我一开始所说的。
spring security 部分
和client 端类似,下面 类 内部 config @Override 方法内部定义过滤规则(有点长)。
@Configuration
@Order(1)
@EnableWebSecurity
@EnableAuthorizationServer
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
@Overrideprotected void configure(HttpSecurity http) throws Exception {// Ant模式通配符匹配http.authorizeRequests().antMatchers("/").permitAll().antMatchers("/login","/oauth/authorize").permitAll().antMatchers("/registration").permitAll().antMatchers("/admin/**").hasAuthority("ADMIN").anyRequest().authenticated().and()
// .csrf().disable().formLogin().loginPage("/login").failureUrl("/login?error=true")
// .defaultSuccessUrl("/admin/home").defaultSuccessUrl(clientUrl)
// .successHandler(successHandler()).usernameParameter("email").passwordParameter("password").and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/").and().exceptionHandling().accessDeniedPage("/access-denied").and().requestMatchers().antMatchers("/", "/login", "/oauth/authorize", "/oauth/confirm_access", "/exit").and().authorizeRequests().antMatchers("/webjars/**").permitAll().anyRequest().authenticated();}
可能有冗余,因为我参考了好多项目,所以先都拿过来用了,也可以看出我这之间尝试的次数之多,真的是这里的配置如果没有人来指点,要话费整个项目编写的整整一半时间!这个绝非危言耸听。一行行来讲,和client 一样的就不讲了
.antMatchers("/admin/**").hasAuthority("ADMIN").anyRequest()
.authenticated() 是/admin/ 开始的网址 需要 “ADMIN” 角色才能访问,这个未来单点需要管理员界面就可以用到了
.formLogin().loginPage("/login").failureUrl("/login?error=true")
// .defaultSuccessUrl("/admin/home").defaultSuccessUrl(clientUrl) 可以看到fromLogin() 指设定登录界面,如果后面没有跟 .loginPage(xxx)那么就到默认登录页面去了,我们是自定义了一个login页面(因为默认页面没有注册功能) 其实好多网上教程都是让你进的默认login界面,这样学的人就不能进行一些功能上的扩展了。 进而我们设置 失败界面failureUrl、成功路径defaultSuccessUrl 注意:这个成功路径必须是我们client 端的响应路径 即应该跳转到的 localhost:7778/ui/secure 这样才能回去,有的项目不用设置也可以回去,这个我没有试过,但就是回不去。。
.usernameParameter("email")
.passwordParameter("password") 这两行制定了我们验证用户的方式,表明需要 用 email 和 password 来验证,在哪里验证? 其实,你一开始是否有疑问,为什么我们可以自定义login登录页面,也可以使用默认的login页面,这是因为,无论选择何种,最后都需要发送两条信息 post 到 auth-server的 /login 地址,注意,是post 不是get get /login 返回的是login 页面, post /login 是将输入的数据发送到 /login 接口 至于这两条数据的id (待会儿 thymeleaf 里面需要设置)即分别是 “email”,和 “password” ,默认情况下你也可以进入默认登录页面查看两者id,以上,我觉得应该描述清楚这两行的作用了。
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/").and().exceptionHandling()
.accessDeniedPage("/access-denied") 这里呢,又是 一串重定向。。不过这里是以后单点管理端可以使用的退出设置(和客户端退出不一样!!! ,听到这里你是不是也晕了,上面这个没用到就不讲了,客户端退出过程先讲)
这个退出按钮代码:
可以看到post 到了 /logout 这个接口地址,其实这个接口(client 项目中) 和上面讲的 /login 接口都是 spring security 提供给我们的,我们只需要配置 还记得上面我将client 项目 config 函数 里的 http配置吗,最后还有 退出的配置,下面红框
设定退出成功 url 为 logoutUrl 这个是在yml 文件里的值(不懂? spring boot @Value 注解了解下)
我们看到是auth-server 地址了,也就是说,退出会跳转到这个地址 localhost:7777/auth/exit
然后我们再来看 auth-server 的 exit 接口 ,在logoutController 类里
@Controllerpublic class LogoutController {@RequestMapping("/exit")public void exit(HttpServletRequest request, HttpServletResponse response) {System.out.println("here is exit");// token can be revoked here if needed wonderful!!!// request obtain the token information and so onnew SecurityContextLogoutHandler().logout(request, null, null);try {response.sendRedirect(request.getHeader("referer"));// 从哪来回哪去,经过上一步肯定还要验证的// in ths case the header's referrer is 8080/notes// and because of the revoking token this url is not authenticated so it redirect to auth-server again// again key in username and password.} catch (IOException e) {e.printStackTrace();}}
}
看的懂英文就看,这个退出还是参考国外一个大神的思路,new SecurityContextLogoutHandler().logout(request, null, null); 这一行,其实,就把auth-server 里的相应session里的token 给清空了,设为了null, 可见之后用户在访问 需权限的网站就会 cookie里的信息不匹配无法访问, 然后try 块里 sendRedirect 方法 也是重定向,到 request.getHeader(“referer") 意思是请求头里的referer 其实就是从哪来回哪去,http header 里的referer 指的就是这个网页之前的网址地址,我们使用浏览器用的返回按钮,其实找的就是这个referer,满满的知识点啊。。
这个referer 是什么呢,来一张截图: (我按了上面的退出 按钮)
过程如上,logout -> exit -> secure (就是那个referer)--> login
为什么会从secure 重定向到 login呢 ,你是不是懵逼了?
其实,从那一句new SecurityContextLogoutHandler().logout(request, null, null) 你就没有权力再次访问 secure 页面了,所以一个没有权限的你,就被整个世界安排到了 login页面,还记得 client 里的http配置吗 我们只给 “/” “/login**” 这两个地址 设置了不需要权限的访问,其他网址访问需要授权,未授权则到auth-server 统一授权了才能再进去,可能会有人问,如果client 客户端有多个了怎么办?
事实上,我们可以定义多个返回值,根据不同的客户端,手累,你们看一下这个网页里怎么设置的 里面截取一段
哦,我现在也知道,这正是我不需要设置 .defaultSuccessUrl(clientUrl) 的原因了,它(框架)再确认账号密码争取后 自动选择跳转的路劲,为什么是这个 xxx/login 是因为 我们没输入账号密码之前, 点击访问secure 页面时
,还是之前的截图
我们看到他是经过了 secure(被阻止)--》 /ui/login (就是中间过程)--》/authorize
我们就是回到了这个 中间过程,然后再进入secure页面。
还有一部分 SecurityConfiguation 类,是设置 Oauth2 的 ,也是极其重要,这里挑选一个最重要配置函数
@Autowiredprivate DataSource dataSource;@Value("${spring.queries.users-query}")private String usersQuery;@Value("${spring.queries.roles-query}")private String rolesQuery;
@Overrideprotected void configure(AuthenticationManagerBuilder auth)throws Exception {
// auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());auth.jdbcAuthentication().usersByUsernameQuery(usersQuery).authoritiesByUsernameQuery(rolesQuery).dataSource(dataSource).passwordEncoder(bCryptPasswordEncoder);// auth.userDetailsService(getUserDetailsService()).passwordEncoder(bCryptPasswordEncoder);
// auth.parentAuthenticationManager(authenticationManager)
// .userDetailsService(customerUserDetailsService);}
这里用了 jdbcAuthentication() 方法,然后两条语句也是自己写的,说起来其实就是查找用户姓名和角色,具体再配置文件里
# ==============================================================
# = Spring Security / Queries for AuthenticationManagerBuilder
# ==============================================================
spring.queries.users-query=select email, password, active from user where email=?
spring.queries.roles-query=select u.email, r.role from user u inner join user_role ur on(u.user_id=ur.user_id) inner join role r on(ur.role_id=r.role_id) where u.email=?
其实这里有很多认证方法,包括UserDetailService 等等,我之前看到简书里面有个人写的不错,到时候把他的文章链接也放到这里来好了,我就不再赘述。简书文章:Spring Security(3)——进阶篇(自定义身份验证)(上)
还有很重要的一部分 Resource Server
记住,这个部分,我在国内还没看到有人提过具体事项
因为我在做单点登录的过程中,遇到了登录之后,跳转不回之前页面的问题,我在国内查找资料毫无进展,还在stackoverflow 上面提问了, 我的问题,结果在之后一次查阅资料的时候,也是在stackoverflow上找到了解决方法
就是这个回答,让我恍然大悟,我在配置 自己的ResourceServer 类即下图,没有加上@EnableResourceServer
这个因为Oauth2 版本问题? 我之前在国外网站学习的时候,是没有这个注解的,比如 这个网站,有时需要翻墙才能访问
这个也算一个大坑了。
所以,踩了这个坑,我才了解了这个resource server 存储,并可以从中获取用户端的 cookie 信息,取出并与auth-server 进行比对等一系列之后才会完成登录之后的client、auth-server 用户数据一致性。
虽然配置所需代码就那么几行。。。
thymeleaf部分
我感觉讲了好多,这个部分我就很简单的讲讲。
thyme leaf 和 spring boot 结合好,就体现在,你可以使用 modelAndView 对象,将数据呈现在对应的view(html页面) 上面
比如get 一个 registration 页面
对应的viewname = registration, 那么这个就是 registration.html 文件了,可以看到加了一个“user” 的信息进去,那么我们就可以再相应的html文件里获取到这个信息,这里其实使用了一个表单
到时候表单发送,就把整个user 对象发送到对应接口
视图层面和 controller 层的交互就都是类似的实现,上面的例子还是比较复杂的一种,简单的单独加个内容什么的,项目里面也是很好找的。
总结:
我感觉还有好多知识点没有讲,实在是东西有点复杂,我本人也是还在研究阶段。
日后再有改进,或有其它任何问题,继续再更
over