文件目录结构:
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/03d285619d5f2f9e4575790c64bfb86b.png)
基本准备
基础页面
所有页面均使用的 Bootstrap
我们最后实现的效果是:
一个视频首页、先判断是否登录,没有登录的跳转至登录页面;
登录后,有两种身份:普通用户、VIP用户,每种身份有不同的权限;
登录后可显示用户名且可以退出;
-
登录页面(login.html)
登录页面就用我们之前的国际化即可https://blog.csdn.net/weixin_44162239/article/details/115414902 -
视频首页(index.html)
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>首页</title>
<link href="//cdn.staticfile.org/twitter-bootstrap/3.0.1/css/bootstrap.min.css" rel="stylesheet">
<script type="text/javascript" src="//cdn.staticfile.org/jquery/2.0.0/jquery.min.js"></script>
<script type="text/javascript" src="//cdn.staticfile.org/twitter-bootstrap/3.0.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
<li>
<a th:href="@{/login}">登录</a>
</li>
</ul>
</div>
</nav>
<div class="alert alert-danger" style="margin-top: 65px;" th:if="${param.logout}">您已退出登录</div>
<div class="carousel slide" id="carousel-351237">
<ol class="carousel-indicators">
<li data-slide-to="0" data-target="#carousel-351237">
</li>
<li data-slide-to="1" data-target="#carousel-351237">
</li>
<li data-slide-to="2" data-target="#carousel-351237" class="active">
</li>
</ol>
<div class="carousel-inner">
<div class="item">
<img alt="" th:src="@{/login/images/mt.jpg}" />
</div>
<div class="item">
<img alt="" th:src="@{/login/images/xw.jpg}" />
</div>
<div class="item active">
<img alt="" th:src="@{/login/images/lx.jpg}" />
</div>
</div> <a class="left carousel-control" href="#carousel-351237" data-slide="prev"><span class="glyphicon glyphicon-chevron-left"></span></a> <a class="right carousel-control" href="#carousel-351237" data-slide="next"><span class="glyphicon glyphicon-chevron-right"></span></a>
</div>
</div>
</div>
<div class="row clearfix">
<div class="col-md-6 column">
<h3>
向往的生活 第五季
</h3>
<p>
《向往的生活》是由湖南卫视推出的大型生活服务纪实节目。节目记录了何炅、黄磊、张艺兴、彭昱畅、张子枫等人一起守拙归园田蘑菇屋,为观众带来一幅“自力更生,自给自足,温情待客,完美生态”的生活画面。
</p>
<a th:href="@{details/common/1}">点击观看</a>
</div>
<div class="col-md-6 column">
<h3>
明星大侦探之名侦探学院 第二季
</h3>
<p>
《明星大侦探之名侦探学院 第二季》为《明星大侦探》兄弟篇,节目邀请了颜值与智商兼具的学霸少年进行实景推理“剧本杀”,共度欢乐爆笑的合宿生活,上演高能脑力对决。
</p>
<a th:href="@{details/vip/1}">点击观看</a>
</div>
</div>
</div>
</body>
</html>
- 只vip可看的视频页面(details/vip/1.html)
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>vip</title>
<link href="//cdn.staticfile.org/twitter-bootstrap/3.0.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="row clearfix">
<div class="col-md-2 column">
<img th:src="@{/login/images/mx.jpg}" />
</div>
<div class="col-md-6 column" style=" margin-left: 93px;">
<h3>
明星大侦探之名侦探学院 第二季
</h3>
<p class="lead">
《明星大侦探之名侦探学院 第二季》为《明星大侦探》兄弟篇,节目邀请了颜值与智商兼具的学霸少年进行实景推理“剧本杀”,共度欢乐爆笑的合宿生活,上演高能脑力对决。
</p>
</div>
<div class="col-md-4 column">
</div>
</div>
</div>
</div>
</div>
</body>
</html>
- 普通用户可看的视频页面(details/common/1.html)
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>common</title>
<link href="//cdn.staticfile.org/twitter-bootstrap/3.0.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="row clearfix">
<div class="col-md-2 column">
<img th:src="@{/login/images/bg.jpg}" />
</div>
<div class="col-md-6 column" style="margin-left: 93px;">
<h3>
向往的生活 第五季
</h3>
<p class="lead">
《向往的生活》是由湖南卫视推出的大型生活服务纪实节目。节目记录了何炅、黄磊、张艺兴、彭昱畅、张子枫等人一起守拙归园田蘑菇屋,为观众带来一幅“自力更生,自给自足,温情待客,完美生态”的生活画面。
</p>
</div>
<div class="col-md-4 column">
</div>
</div>
</div>
</div>
</div>
</body>
</html>
基础数据库
- t_customer(用户表)
username(用户名必须唯一)、password(密码,必须是加密后的)、valid(用户是否合法,tinyint类型)这三个字段必须存在
- t_authority(用户权限表)
authority字段的值必须为ROLE_***
- t_customer_authority(用户权限关联表)
/* 数据库叫 springboot */
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for t_authority
-- ----------------------------
DROP TABLE IF EXISTS `t_authority`;
CREATE TABLE `t_authority` (
`id` int(20) NOT NULL AUTO_INCREMENT,
`authority` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_authority
-- ----------------------------
INSERT INTO `t_authority` VALUES ('1', 'ROLE_common');
INSERT INTO `t_authority` VALUES ('2', 'ROLE_vip');
-- ----------------------------
-- Table structure for t_customer
-- ----------------------------
DROP TABLE IF EXISTS `t_customer`;
CREATE TABLE `t_customer` (
`id` int(20) NOT NULL AUTO_INCREMENT,
`username` varchar(200) DEFAULT NULL,
`password` varchar(200) DEFAULT NULL,
`valid` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_customer
-- ----------------------------
INSERT INTO `t_customer` VALUES ('1', 'shitou', '$2a$10$5ooQI8dir8jv0/gCa1Six.GpzAdIPf6pMqdminZ/3ijYzivCyPlfK', '1');
INSERT INTO `t_customer` VALUES ('2', '李四', '$2a$10$5ooQI8dir8jv0/gCa1Six.GpzAdIPf6pMqdminZ/3ijYzivCyPlfK', '1');
/* 对应密码均为 123456*/
-- ----------------------------
-- Table structure for t_customer_authority
-- ----------------------------
DROP TABLE IF EXISTS `t_customer_authority`;
CREATE TABLE `t_customer_authority` (
`id` int(20) NOT NULL AUTO_INCREMENT,
`customer_id` int(20) DEFAULT NULL,
`authority_id` int(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_customer_authority
-- ----------------------------
INSERT INTO `t_customer_authority` VALUES ('1', '1', '1');
INSERT INTO `t_customer_authority` VALUES ('2', '2', '2');
页面访问的控制类
package com.zknu.inter.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@Controller
public class MovieController {
@GetMapping("/details/{type}/{path}")
public String toMovie(@PathVariable("type") String type,@PathVariable("path") String path){
return "details/"+type+"/"+path;
}
// 访问 details下不同权限的页面
}
自定义用户身份认证
身份认证就是判断你输入的用户名和密码是否正确
JDBC身份认证
JDBC认证是用数据库中的信息进行认证
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
连接数据库配置
和以前一样,不写了
进行身份认证(SecurityConfig)
重写 WebSecurityConfigurerAdapter 中的 configure 方法
package com.zknu.inter.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import javax.sql.DataSource;
@EnableWebSecurity // 开启MVC Security 安全支持
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 对密码进行编码
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String userSQL = "select name,password,valid from t_customer where name = ?";
String authoritySQL = "select c.name,a.authority from t_customer c,t_authority a,t_customer_authority ca " +
"where ca.customer_id=c.id and ca.authority_id=a.id and c.name=?";
// 两句SQL语句基本固定 ,必须至少返回为这些值
auth.jdbcAuthentication().passwordEncoder(encoder).dataSource(dataSource)
.usersByUsernameQuery(userSQL).authoritiesByUsernameQuery(authoritySQL);
}
}
此时我们访问 http://localhost:8080
发现会自动跳转至 Security 自带的登录页面
登录成功会进去首页
登录失败会报错
登录成功后发现我们不论哪一种身份都可访问所有页面,所以我们接下来进行用户自定义访问控制,控制页面的访问权限
自定义用户授权管理
自定义用户访问控制
接着重写 configure 方法,两个方法的参数不一样
package com.zknu.inter.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import javax.sql.DataSource;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String userSQL = "select name,password,valid from t_customer where name = ?";
String authoritySQL = "select c.name,a.authority from t_customer c,t_authority a,t_customer_authority ca " +
"where ca.customer_id=c.id and ca.authority_id=a.id and c.name=?";
auth.jdbcAuthentication().passwordEncoder(encoder).dataSource(dataSource)
.usersByUsernameQuery(userSQL).authoritiesByUsernameQuery(authoritySQL);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() // 开启请求访问限制
.antMatchers("/").permitAll() // 允许对index页面直接访问
.antMatchers("/login/**").permitAll()
// 对 /login/ 下的所有文件允许访问(页面的静态资源文件)
.antMatchers("/details/common/**").hasAnyRole("common","vip")
// 允许权限为 common 和 vip 的用户访问 common下的所有文件
.antMatchers("/details/vip/**").hasRole("vip")
// 仅允许权限为 vip 的用户访问 vip 下的所有文件
.anyRequest().authenticated()
/*
匹配已经登录认证的用户*/
.and()
// 功能连接
.formLogin();
// 开启基于表单的用户登陆
}
}
静态资源目录结构
此时我们访问 http://localhost:8080
发现并没有自动跳到登录页面,因为我们对该页面进行了放行。
任意点击一个链接,发现会跳至登录页面,因为当我们请求details下的页面时,发现我们没有登录,所以会被拦截并跳转至登录页面。我们登录shitou的账号密码。
登录成功后会跳到刚刚想要访问的页面
因为shitou 的权限时普通用户,因此我们访问vip页面时会报403错误(无访问权限)
加了权限访问之后,测试会变得很麻烦,因为很多页面会变得没有访问权限,所以之后测试的时候报错首先考虑是否有访问权限。
自定义403页面(访问权限不够时的页面)
但是这个页面时非常不美观的,所以我们可以写一个403页面作为访问权限不够时的提示页面
403.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>暂无访问权限</title>
<link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container" style="margin-top: 5%">
<div class="jumbotron">
<p>开通会员后观看</p>
<p><a class="btn btn-primary btn-lg" role="button">
立即开通</a>
</p>
</div>
</div>
</body>
</html>
403页面访问控制器
@GetMapping("/403")
public String authority(){
return "403";
}
在用户访问控制中加上
.and().exceptionHandling().accessDeniedPage("/403")
就可以在权限不够的时候访问至403页面了。
此时我们再用shitou 用户访问 vip 页面:
发现我们自定义的页面已经取代了原先那个满是提示报错的页面
自定义登录页面
但我们此时的登录页面仍为Security的默认页面,不太合适吧,所以我们自己自定义一个。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录</title>
<link th:href="@{/login/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/login/css/login.css}" rel="stylesheet">
</head>
<body>
<form class="sign" method="post" th:action="@{/login}">
<img class="img-circle img" th:src="@{/login/images/hr.jpg}">
<h2 th:text="#{login.tip}">请登录</h2>
<!-- #{login.tip} 这些是之前写的国际化的内容 不要和我一样忘记了,都不知道是什么东西,笑死 -->
<div class="alert alert-danger" th:if="${param.error}">用户名或密码错误!</div>
<!-- ${param.error} 接收登录失败后传来的参数,在下面configure方法里 -->
<div class="form-group">
<input type="text" class="form-control user" name="username" th:placeholder="#{login.username}" placeholder="用户名">
</div>
<div class="form-group">
<input type="password" class="form-control user" name="password" th:placeholder="#{login.password}" placeholder="密码">
</div>
<button class="btn btn-primary btn-block user" type="submit" th:text="#{login.button}">登录</button>
<p class="text-center year">
@
<span th:text="${currentYear}">2018</span>
-
<span th:text="${currentYear}+1">2019</span>
</p>
<a class="lang" th:href="@{/login(lang='zh_CN')}"> 中文 </a>
<a class="lang" th:href="@{/login(lang='en_US')}"> English </a>
</form>
</body>
</html>
继续增加 configure 方法中的内容
package com.zknu.inter.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import javax.sql.DataSource;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String userSQL = "select name,password,valid from t_customer where name = ?";
String authoritySQL = "select c.name,a.authority from t_customer c,t_authority a,t_customer_authority ca " +
"where ca.customer_id=c.id and ca.authority_id=a.id and c.name=?";
auth.jdbcAuthentication().passwordEncoder(encoder).dataSource(dataSource)
.usersByUsernameQuery(userSQL).authoritiesByUsernameQuery(authoritySQL);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login/**").permitAll()
.antMatchers("/details/common/**").hasAnyRole("common","vip")
.antMatchers("/details/vip/**").hasRole("vip")
.anyRequest().authenticated()
.and().formLogin()
.and().exceptionHandling().accessDeniedPage("/403");
http.formLogin().
loginPage("/login").permitAll()
// 登录页面定为 login 页面,可以直接是html页面,也可以是该html的访问控制类,但上面那个403必须为控制类
.usernameParameter("username").passwordParameter("password")
/* 接收登录是提交的用户名和密码 必须与前端表单中对应 input的name一致*/
.defaultSuccessUrl("/",true)
// 默认登录成功后的页面
.failureUrl("/login?error");
// 登录失败后访问的页面
}
}
解释一下这句话
.defaultSuccessUrl("/",true)
为什么加 true:
在我们上一步的测试中发现,我们首先访问 首页,任意点击一个页面后会跳转至登录页面,登录成功后会返回至我们之前访问的那个页面,如果我们不加true,默认成功后的页面会优先访问我们登录前想访问的那个页面;
加true 后会优先访问我们自己定义的访问页面。
defaultSuccessUrl与successForwardUrl
功能一样,都是设置认证成功后的跳转路径。
但是 successForwardUrl 是转发过来,在Controller中需要加一个重定向
https://blog.csdn.net/qq_34975710/article/details/110232128
运行结果
登录页面
登录报错
用户退出登录
有了登录就得有退出登录
在首页中增加注销按钮
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<meta charset="UTF-8">
<title>首页</title>
<link href="//cdn.staticfile.org/twitter-bootstrap/3.0.1/css/bootstrap.min.css" rel="stylesheet">
<script type="text/javascript" src="//cdn.staticfile.org/jquery/2.0.0/jquery.min.js"></script>
<script type="text/javascript" src="//cdn.staticfile.org/twitter-bootstrap/3.0.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
<li>
<a th:href="@{/login}">登录</a>
</li>
<li>
<form class="logout" th:action="@{/logout}" method="post" style="padding: 5px 15px;">
<input type="submit" class="btn btn-warning" value="注销">
</form>
</li>
<!-- 必须为post方法 -->
</ul>
</div>
</nav>
<div class="alert alert-danger" style="margin-top: 65px;" th:if="${param.logout}">您已退出登录</div>
<div class="carousel slide" id="carousel-351237">
<ol class="carousel-indicators">
<li data-slide-to="0" data-target="#carousel-351237">
</li>
<li data-slide-to="1" data-target="#carousel-351237">
</li>
<li data-slide-to="2" data-target="#carousel-351237" class="active">
</li>
</ol>
<div class="carousel-inner">
<div class="item">
<img alt="" th:src="@{/login/images/mt.jpg}" />
</div>
<div class="item">
<img alt="" th:src="@{/login/images/xw.jpg}" />
</div>
<div class="item active">
<img alt="" th:src="@{/login/images/lx.jpg}" />
</div>
</div> <a class="left carousel-control" href="#carousel-351237" data-slide="prev"><span class="glyphicon glyphicon-chevron-left"></span></a> <a class="right carousel-control" href="#carousel-351237" data-slide="next"><span class="glyphicon glyphicon-chevron-right"></span></a>
</div>
</div>
</div>
<div class="row clearfix">
<div class="col-md-6 column">
<h3>
向往的生活 第五季
</h3>
<p>
《向往的生活》是由湖南卫视推出的大型生活服务纪实节目。节目记录了何炅、黄磊、张艺兴、彭昱畅、张子枫等人一起守拙归园田蘑菇屋,为观众带来一幅“自力更生,自给自足,温情待客,完美生态”的生活画面。
</p>
<a th:href="@{details/common/1}">点击观看</a>
</div>
<div class="col-md-6 column">
<h3>
明星大侦探之名侦探学院 第二季
</h3>
<p>
《明星大侦探之名侦探学院 第二季》为《明星大侦探》兄弟篇,节目邀请了颜值与智商兼具的学霸少年进行实景推理“剧本杀”,共度欢乐爆笑的合宿生活,上演高能脑力对决。
</p>
<a th:href="@{details/vip/1}">点击观看</a>
</div>
</div>
</div>
</body>
</html>
接着增加configure方法
http.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/?logout=true");
测试结果
获取登录用户信息
使用SecurityContextHolder获取
在控制类中增加
package com.zknu.inter.controller;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MovieController {
@GetMapping("/details/{type}/{path}")
public String toMovie(@PathVariable("type") String type,@PathVariable("path") String path){
return "details/"+type+"/"+path;
}
@GetMapping("/403")
public String authority(){
return "403";
}
@GetMapping("/getUserName")
@ResponseBody
public void user(){
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
UserDetails principal = (UserDetails) authentication.getPrincipal();
System.out.println("name:"+principal.getUsername());
}
}
因为登录后登录信息就会保存在浏览器中,只以只要不重启或关闭浏览器,你的登录信息就会一直在,所以也提示了我们如果没有重启启动类的情况下想换用户登录,需要清空浏览器的cookie
我用的是QQ浏览器,清理cookie的方法就是
测试方法
先登录,登录后再访问http://localhost:8080/getUserName
控制台会输出当前的用户名
用Security的sec标签获取
增加依赖
必须加版本号
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
sec标签和thymeleaf 的th一样,都是用在前端的。
将前端中登录和注销那里变成这样
记得加
xmlns:sec=“http://www.thymeleaf.org/thymeleaf-extras-springsecurity5”
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<meta charset="UTF-8">
<title>首页</title>
<link href="//cdn.staticfile.org/twitter-bootstrap/3.0.1/css/bootstrap.min.css" rel="stylesheet">
<script type="text/javascript" src="//cdn.staticfile.org/jquery/2.0.0/jquery.min.js"></script>
<script type="text/javascript" src="//cdn.staticfile.org/twitter-bootstrap/3.0.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
<li sec:authorize="isAuthenticated()">
<a sec:authentication="name"></a>
</li>
<li sec:authorize="isAuthenticated()">
<form class="logout" th:action="@{/logout}" method="post" style="padding: 5px 15px;">
<input type="submit" class="btn btn-warning" value="注销">
</form>
</li>
<li sec:authorize="isAnonymous()">
<a th:href="@{/login}">登录</a>
</li>
<!-- <li>
<a th:href="@{/login}">登录</a>
</li>
<li>
<form class="logout" th:action="@{/logout}" method="post" style="padding: 5px 15px;">
<input type="submit" class="btn btn-warning" value="注销">
</form>
</li>-->
</ul>
</div>
</nav>
<div class="alert alert-danger" style="margin-top: 65px;" th:if="${param.logout}">您已退出登录</div>
<div class="carousel slide" id="carousel-351237">
<ol class="carousel-indicators">
<li data-slide-to="0" data-target="#carousel-351237">
</li>
<li data-slide-to="1" data-target="#carousel-351237">
</li>
<li data-slide-to="2" data-target="#carousel-351237" class="active">
</li>
</ol>
<div class="carousel-inner">
<div class="item">
<img alt="" th:src="@{/login/images/mt.jpg}" />
</div>
<div class="item">
<img alt="" th:src="@{/login/images/xw.jpg}" />
</div>
<div class="item active">
<img alt="" th:src="@{/login/images/lx.jpg}" />
</div>
</div> <a class="left carousel-control" href="#carousel-351237" data-slide="prev"><span class="glyphicon glyphicon-chevron-left"></span></a> <a class="right carousel-control" href="#carousel-351237" data-slide="next"><span class="glyphicon glyphicon-chevron-right"></span></a>
</div>
</div>
</div>
<div class="row clearfix">
<div class="col-md-6 column">
<h3>
向往的生活 第五季
</h3>
<p>
《向往的生活》是由湖南卫视推出的大型生活服务纪实节目。节目记录了何炅、黄磊、张艺兴、彭昱畅、张子枫等人一起守拙归园田蘑菇屋,为观众带来一幅“自力更生,自给自足,温情待客,完美生态”的生活画面。
</p>
<a th:href="@{details/common/1}">点击观看</a>
</div>
<div class="col-md-6 column">
<h3>
明星大侦探之名侦探学院 第二季
</h3>
<p>
《明星大侦探之名侦探学院 第二季》为《明星大侦探》兄弟篇,节目邀请了颜值与智商兼具的学霸少年进行实景推理“剧本杀”,共度欢乐爆笑的合宿生活,上演高能脑力对决。
</p>
<a th:href="@{details/vip/1}">点击观看</a>
</div>
</div>
</div>
</body>
</html>
isAuthenticated():判断用户是否已登录,已登录返回 true
isAuthenticated():判断用户是否未登录,未登录返回true
这两个方法的前缀都是 sec:authorize(注意! 是authorize,不是authority,刚开始我一直写的都是 ty 导致我运行一直不对,然后发现我自己拼错了,英语不好的悲哀啊,唉)
sec:authentication=“name” :获取当前用户的用户名
其他的看这里
https://www.cnblogs.com/jpfss/p/8669000.html
测试结果
访问首页,没有登录时只显示登录按钮,不显示注销按钮;
登陆后显示用户名和注销按钮
注销后又只显示登录按钮
该功能有也可以用上面那个SecurityContextHolder完成
注意,th:if 没有 else,用th:unless替代
将thymeleaf用在js里面
方便页面布局啊跳转啊传参啊什么的。
我们将登录失败传的 error 加一个值 true
在login页面加js,在控制台打印
<script th:inline="javascript">
var er = [[${param.error}]];
console.log(er);
</script>
注意js一定要在html标签内,因为引用的th链接是在html标签上引用的
控制台打印,发现我们接收到的参数都是数组形式。
更多内容可参考
https://blog.csdn.net/mygzs/article/details/52667897