admin
静态资源导入:
页面:
如果想要使用 thymeleaf 就一定要在 templates 目录下。
创建 Controller 类,访问到登录页面
@Controller
public class IndexController {
@GetMapping(value = {"/","login"})
public String loginPage(){
return "login";
}
}
- 如果不定义,默认会走到 resources 的 static 下。
- 现在是定向的把路径导到了 resources 的 templates 下。
用 thymeleaf 对前端模板进行渲染。
导入依赖
xmlns:th="http://www.thymeleaf.org"
th:(开头) $(动态的为XXXX进行赋值)
<p th:text="${xxxx}"></p>
这里是 post 是因为 在 Controller 里已经 get 了,localhost 走到了 login 的页面。
th:action="@(表示跳转){(这里需要写跳转参数,指的是跳到前端的哪一个方法里面去,thymeleaf 语法)}"
登录成功去首页的方法:
@PostMapping("/login")
public String homepage(User user, Model model , HttpSession session){
return "homepage";
}
导向的是静态页面,看似是跳转实际上是被写死的。我们要做的是经过 Controller 动态的返回页面。
重复提交表单问题
因为是 @PostMapping("/login") ,解决的话需要多加一个跳转。
@PostMapping("/login")
public String homepage(){
// 登录成功之后,重定向请求页面
// 避免POST表单重复提交
return "redirect:/homepage.html";
}
@GetMapping("/homepage.html")
public String index(){
return "homepage";
}
- 重复表单提交没有了。
- 但是网址变成 html 。之前是 /login,@PostMapping 方式请求。
- /homepage.html 看似是 url 地址,好像到了前端页面,但是不是。是先到 Controller 里面,如果没有才 会访问到静态资源里。
- @PostMapping 起到了一个桥梁的作用,引到了@GetMapping 避免了重复提交 Post 表单。
- redirect 是重定向,是方法与方法之间的跳转。不是重定向到静态资源 /homepage.html 。是方法路径 /homepage.html 。
页面是需要登录才能访问,不能输入网址就访问,需要做拦截
先做登录
创建User类:(因为设置了lombok所以用注解)
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String username;
private String pwd;
}
前端页面:
<input type="text" name="username" class="form-control" placeholder="User ID" autofocus>
<input type="password" name="pwd" class="form-control" placeholder="Password">
修改Controller类:
private final String AUTHENTICATION_FAILED = "用户名或密码输入错误!";
private final String AUTHORIZATION_FAILED = "无权限访问,请先登录";
@PostMapping("/login")
public String homepage(User user, Model model , HttpSession session){
// 登录成功之后,重定向请求页面
// 避免POST表单重复提交
// 1. 判断用户名或密码是否为空,为空则返回null,程序不再继续往下执行
if( StringUtils.isEmpty(user.getUsername()) || StringUtils.isEmpty(user.getPwd())) return null;
// 2. 用户名和密码是否匹配,验证通过则进入首页,验证失败则重新进入登录页面
if("admin".equals(user.getUsername()) && "admin".equals(user.getPwd())){
session.setAttribute("loginUser", user);
return "redirect:/homepage.html";
}else{
model.addAttribute("msg",AUTHENTICATION_FAILED);
return "login";
}
}
@GetMapping("/homepage.html")
public String index(Model model , HttpSession session){
//1. 判断用户信息是否已经存在于session中,存在: 用户已经成功登录,则不需要重复判断用户信息
Object loginUser = session.getAttribute("loginUser");
//2.session存在,直接进入首页
if(loginUser != null) return "homepage";
//3. session不存在,需要用户重新登录
model.addAttribute("msg",AUTHORIZATION_FAILED);
return "login";
}
isEmpty 源码:
输入对错并不知道,所以需要做一个提示。页面上进行修改
Model:创建一个模型,把值传入模型,反馈到前端。
private final String AUTHENTICATION_FAILED = "用户名或密码输入错误!";
model.addAttribute("msg","AUTHENTICATION_FAILED");
做安全校验,web 开发有个重要的一点,缓存。
登陆成功把信息添加到缓存中,如果判断缓存里有信息,就证明已经认证过了;如果没有在缓存就说明没有认证,拒绝访问。
private final String AUTHORIZATION_FAILED = "无权限访问,请先登录";
// "loginUser" 类似key
session.setAttribute("loginUser", user);
public String index(Model model , HttpSession session){
//1. 判断用户信息是否已经存在于session中,存在: 用户已经成功登录,则不需要重复判断用户信息
Object loginUser = session.getAttribute("loginUser");
//2.session存在,直接进入首页
if(loginUser != null) return "homepage";
//3. session不存在,需要用户重新登录
model.addAttribute("msg",AUTHORIZATION_FAILED);
return "login";
}
插入两个页面
网页直接输入网址访问不到,因为 table 文件夹挡住了路径。
暂时先将网页放在静态目录下面。
网址输入 basic_table.html 发现没有拦截,可以直接访问。
如果按照之前的方法做拦截,每一个网址都需要做一个拦截,这样太麻烦了,也太繁琐了。
所以需要拦截器来拦截
HandlerInterceptor 源码:
public class AppHandlerInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
// 1. 判断用户信息是否已经存在于session中,存在: 用户已经成功登录,则不需要重复判断用户信息
Object loginUser = request.getSession().getAttribute("loginUser");
// 2.session存在,表明不需要拦截
if(loginUser != null) return true;
// 3. session不存在,表明需要拦截掉,用户重新登录
request.setAttribute("msg","无权限访问,请先登录!");
request.getRequestDispatcher("/login").forward(request,response);
return false;
}
}
- HttpServletRequest 和 HttpSession 类似,用 HttpServletRequest 是因为有一些其他方法,比 HttpSession 更好用。
- request.getRequestDispatcher("/login").forward(request,response); 重定向,分发到首页。
拦截器生效,需要添加到自定义的配置类里:
右键点击:Generate
选择 Implement Methods,然后选择方法
查看 addInterceptors 源码,需要传入参数。指向了拦截器
@Configuration
public class ApplicationConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AppHandlerInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/homepage.html","/login","/","/js/**","/css/**","/images/**","/fonts/**" );
}
}
- .addPathPatterns("/**"),表示全部路径都拦截。(不能只有这个,那就啥都看不着了)
- .excludePathPatterns(表示哪些排除在外)
分析模块
- .css文件(样式显示)
- .jss文件(功能响应)
把每个页面重复部分抽取出来,做公共部分
公共部分
thymeleaf 官网翻译:
- 在我们的模板中,我们通常希望包含来自其他模板的部分,如页脚、页眉、菜单等部分…
- 为了做到这一点,Thymeleaf需要我们为包含定义这些部分“片段”,这可以使用 th:fragment 属性来完成。
- 假设我们想给所有的页面添加一个标准的版权页脚,那么我们就创建一个 /WEB-INF/templates/footer.html 包含此代码的文件:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div th:fragment="copy">
© 2011 The Good Thymes Virtual Grocery
</div>
</body>
</html>
两种方法:
<body>
...
<div th:insert="~{footer :: copy}"></div>
</body>
<body>
...
<div th:insert="footer :: copy"></div>
</body>
- ~{} (表明是引用的,类似import)
然后导入:
- replace 是替换
- insert 是插入(会多出来一个 div )
使用 thymeleaf 一定要记得导入依赖:xmlns:th=“http://www.thymeleaf.org”
创建新的Controller类跳转
@Controller
public class TableController {
@GetMapping("/basicTable")
public String basicTable(){
return "/table/basic_table";
}
@GetMapping("/dynamicTable")
public String dynamicTable(){
return "/table/dynamic_table";
}
}
thymeleaf基本语法:
https://www.jianshu.com/p/d1370aeb0881
公共引用
页面导入: