简介
Spring 是非常流行和成功的 Java 应用开发框架,Spring Security 正是 Spring 家族中的成员。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。
于安全方面的两个主要区域是“认证”和“授权”(或者访问控制),一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分,这两点也是 Spring Security 重要核心功能
(1)用户认证指的是:验证某个用户是否为系统中的合法主体,也就是说用户能否访问 该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认 证过程。通俗点说就是系统认为用户是否能登录
(2)用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户 所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以 进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的 权限。通俗点讲就是系统判断用户是否有权限去做某些事情。
SpringSecurity 特点:
- 和 Spring 无缝整合。
- 全面的权限控制。
- 专门为 Web 开发而设计。
- 旧版本不能脱离 Web 环境使用。
- 新版本对整个框架进行了分层抽取,分成了核心模块和 Web 模块。单独
引入核心模块就可以脱离 Web 环境。 - 重量级。
Shiro
Apache 旗下的轻量级权限控制框架。 特点:
- 轻量级。Shiro 主张的理念是把复杂的事情变简单。针对对性能有更高要求
的互联网应用有更好表现。 - 通用性。
- 好处:不局限于 Web 环境,可以脱离 Web 环境使用。
- 缺陷:在 Web 环境下一些特定的需求需要手动编写代码定制
认识SpringSecurity
Spring Security 是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,他可以实现强大的Web安全控制,对于安全控制,我们仅需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理!
记住几个类:
WebSecurityConfigurerAdapter :自定义Security策略AuthenticationManagerBuilder :自定义认证策略@EnableWebSecurity :开启WebSecurity模式
Spring Security的两个主要目标是 “认证” 和 “授权”(访问控制)。
“认证”(Authentication)
身份验证是关于验证您的凭据,如用户名/用户ID和密码,以验证您的身份。
身份验证通常通过用户名和密码完成,有时与身份验证因素结合使用。
“授权” (Authorization)
授权发生在系统成功验证您的身份后,最终会授予您访问资源(如信息,文件,数据库,资金,位置,几乎任何内容)的完全权限。
这个概念是通用的,而不是只在Spring Security 中存在。
SpringSecurity 入门案例
创建一个springboot项目,加入web和security的启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
controller
package com.blb.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("/hello")
public String hello(){
return "hello";
}
}
访问
http://localhost:8080/hello
 默认的用户名:user 密码在项目启动的时候在控制台会打印,注意每次启动的时候密码都回发生变化!   输入玩账户密码后才能进入返回的页面
SpringSecurity 本质是一个过滤器链: 从启动是可以获取到过滤器链:
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFil
ter
org.springframework.security.web.context.SecurityContextPersistenceFilter
org.springframework.security.web.header.HeaderWriterFilter
org.springframework.security.web.csrf.CsrfFilter
org.springframework.security.web.authentication.logout.LogoutFilter
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter
org.springframework.security.web.savedrequest.RequestCacheAwareFilter
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
org.springframework.security.web.authentication.AnonymousAuthenticationFilter
org.springframework.security.web.session.SessionManagementFilter
org.springframework.security.web.access.ExceptionTranslationFilter
org.springframework.security.web.access.intercept.FilterSecurityInterceptor
配置
设置用户名和密码
方式一 通过配置文件
spring:
security:
user:
name: dyk
password: 123456
方式二 通过配置类
@Configuration
public class SecurityConfig1 extends WebSecurityConfigurerAdapter {
@Bean
BCryptPasswordEncoder password(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder passwordEncoder=new BCryptPasswordEncoder();
String password=passwordEncoder.encode("123456");
auth.inMemoryAuthentication().withUser("dyk123").password(password).roles("admin");
}
}
方式三 自定义编写实现类
1.创建配置类,使用UserDetailsService实现类 2.编写实现类,返回User对象,User对象有用户名和密码和操作权限
创建MyUserDetailsService实现UserDetailsService 返回User对象
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("role");
return new User("cb123",new BCryptPasswordEncoder().encode("123"),auths);
}
}
以后用户名密码权限都是通过查询数据库得到的这里我是用集合模拟的假数据
配置类注入MyUserDetailsService
@Configuration
public class SecurityConfig2 extends WebSecurityConfigurerAdapter {
@Bean
BCryptPasswordEncoder password(){
return new BCryptPasswordEncoder();
}
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(password());
}
}
返回值 UserDetails
这个类是系统默认的用户“主体”
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
我们只需要使用 User 这个实体类即可
public class User implements UserDetails, CredentialsContainer {
private static final long serialVersionUID = 550L;
private static final Log logger = LogFactory.getLog(User.class);
private String password;
private final String username;
private final Set<GrantedAuthority> authorities;
private final boolean accountNonExpired;
private final boolean accountNonLocked;
private final boolean credentialsNonExpired;
private final boolean enabled;
public User(String username, String password, Collection<? extends GrantedAuthority> authorities) {
this(username, password, true, true, true, true, authorities);
}
通过查询数据库设置账户密码权限
依赖
这里新引入了mysql,mybatisplus,lombok依赖
<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>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</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>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
配置文件
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db3?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: 123456
thymeleaf:
cache: false
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
创建数据库
CREATE TABLE users(
id INT PRIMARY KEY ,
username VARCHAR(20),
PASSWORD VARCHAR(20)
)CHARSET=utf8
实体类Users
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Users {
private Integer id;
private String username;
private String password;
}
创建接口继承MybatisPlus的BaseMapper
@Repository
@Mapper
public interface UsersMapper extends BaseMapper<Users> {
}
可以使用@Mapper或者在启动类上加入MapperScan
查询数据库
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UsersMapper usersMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
QueryWrapper queryWrapper=new QueryWrapper();
queryWrapper.eq("username",username);
Users users = usersMapper.selectOne(queryWrapper);
if(users==null){
throw new UsernameNotFoundException("用户名不存在");
}
List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("role");
return new User(users.getUsername(),new BCryptPasswordEncoder().encode(users.getPassword()),auths);
}
}
自定义用户登录页面
在配置类中重写protected void configure(HttpSecurity http) 方法
@Configuration
public class SecurityConfig2 extends WebSecurityConfigurerAdapter {
@Bean
BCryptPasswordEncoder password(){
return new BCryptPasswordEncoder();
}
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(password());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/user/login")
.defaultSuccessUrl("/index").permitAll()
.and().authorizeRequests()
.antMatchers("/","/hello","/user/login").permitAll()
.anyRequest().authenticated()
.and().csrf().disable();
}
}
在static目录下新建login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/user/login" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="text" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
注意 表单里面的name标签必须是username和password 原因: 在执行登录的时候会走一个过滤器 UsernamePasswordAuthenticationFilter
controller
@RestController
public class TestController {
@GetMapping("/hello")
public String hello(){
return "hello";
}
@RequestMapping("/index")
public String index(){
return "index";
}
}
 可以发先由于设置了
antMatchers("/","/hello","/user/login").permitAll()
现在不需要登录就能访问/hello了
表单的提交地址要和配置类中的一致
<form action="/user/login" method="post">
http.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/user/login")
.defaultSuccessUrl("/index").permitAll()
并且登录成功后会自动访问controller里的/index路径
基于角色或权限进行访问
我的理解是角色和权限,角色其实是权限的集合
hasAuthority
如果当前的主体具有指定的权限,则返回 true,否则返回 false
在配置类设置当前访问路径需要有那些权限
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/user/login")
.defaultSuccessUrl("/index").permitAll()
.and().authorizeRequests()
.antMatchers("/","/hello","/user/login").permitAll()
.antMatchers("/").hasAuthority("admin")
.anyRequest().authenticated()
.and().csrf().disable();
}
在UserDetailsService,把返回user对象设置权限
当这边设置的权限是role而不是admin时
List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("role");
return new User(users.getUsername(),new BCryptPasswordEncoder().encode(users.getPassword()),auths);
 当把当前登录用户权限设置为admin时即可访问 /
List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
return new User(users.getUsername(),
new BCryptPasswordEncoder().encode(users.getPassword()),auths);

hasAnyAuthority
如果当前的主体有任何提供的角色(给定的作为一个逗号分隔的字符串列表)的话,返回true.
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/user/login")
.defaultSuccessUrl("/index").permitAll()
.and().authorizeRequests()
.antMatchers("/","/hello","/user/login").permitAll()
.antMatchers("/").hasAuthority("admin")
.antMatchers("/hello").hasAnyAuthority("admin,manager")
.anyRequest().authenticated()
.and().csrf().disable();
}
List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
return new User(users.getUsername(),
new BCryptPasswordEncoder().encode(users.getPassword()),auths);
和上面那个方法效果几乎一样,这里就不给出截图了
hasRole
如果用户具备给定角色就允许访问,否则出现 403。 如果当前主体具有指定的角色,则返回 true
@Configuration
public class SecurityConfig3 extends WebSecurityConfigurerAdapter {
@Bean
BCryptPasswordEncoder password(){
return new BCryptPasswordEncoder();
}
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(password());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/user/login")
.defaultSuccessUrl("/index").permitAll()
.and().authorizeRequests()
.antMatchers("/","/hello","/user/login").permitAll()
.antMatchers("/").hasRole("teacher")
.anyRequest().authenticated()
.and().csrf().disable();
}
}
注意给用户添加角色要加上ROLE_
List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_teacher");
return new User(users.getUsername(),
new BCryptPasswordEncoder().encode(users.getPassword()),auths);
hasAnyRole
表示用户具备任何一个条件都可以访问。 给用户添加角色
基本作用和上面一样,这里就不做过多描述,区别就是只要具备其中一个角色就能访问该路径
自定义没有权限访问页面
@Configuration
public class SecurityConfig3 extends WebSecurityConfigurerAdapter {
@Bean
BCryptPasswordEncoder password(){
return new BCryptPasswordEncoder();
}
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(password());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling().accessDeniedPage("/403.html");
http.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/user/login")
.defaultSuccessUrl("/index").permitAll()
.and().authorizeRequests()
.antMatchers("/","/hello","/user/login").permitAll()
.antMatchers("/").hasRole("teacher")
.anyRequest().authenticated()
.and().csrf().disable();
}
}
403.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>403页面</title>
<style>
body {
width: 100%;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
family: font;
background-image: linear-gradient(45deg, #f6d200 25%, #181617 25%, #181617 50%, #f6d200 50%, #f6d200 75%, #181617 75%, #181617 100%);
}
h1 {
text-transform: uppercase;
background: repeating-linear-gradient(
45deg,
#f6d200 ,
#f6d200 10px,
#181617 10px,
#181617 20px
);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-size: 384px;
margin: 0;
line-height: .7;
position: relative;
:before,
:after{
content: "Caution";
background-color: #f6d200;
color: #181617;
border-radius: 10px;
font-size: 35px;
position: absolute;
padding: 31px;
text-transform: uppercase;
font-weight: bold;
-webkit-text-fill-color: #181617;
left: 50%;
top: 50%;
transform: translate(-50%, -50%) rotate(20deg);
}
:before {
content: "";
padding: 70px 130px;
background: repeating-linear-gradient(45deg, #f6d200, #f6d200 10px, #181617 10px, #181617 20px);
box-shadow: 0px 0px 10px #181617;
}
span:before,
span:after{
content: "";
width: 8px;
height: 8px;
background: #757575;
color: #757575;
border-radius: 50%;
position: absolute;
bottom: 0;
margin: auto;
top: 20%;
z-index: 3;
box-shadow: 0px 60px 0 0px;
}
span:before {
left: 37%;
transform: rotate(22deg);
top: -44%;
}
span:after {
right: 34%;
transform: rotate(22deg);
top: 3%;
}
}
</style>
</head>
<body>
<h1 class="text"><span>403</span></h1>
</body>
</html>
注解的使用
@Secured
判断是否具有角色,另外需要注意的是这里匹配的字符串需要添加前缀“ROLE_“。 使用注解先要开启注解功能!
在启动类(或配置类)开启注解
@EnableGlobalMethodSecurity(securedEnabled = true)
在controller的方法上使用注解,设置角色
@RequestMapping("/insert")
@Secured({"ROLE_sale","ROLE_manager"})
public String insert()
{
return "insert";
}
UserDetailsService设置用户角色
List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_manager");
return new User(users.getUsername(),
new BCryptPasswordEncoder().encode(users.getPassword()),auths);

@PreAuthorize
@PreAuthorize:注解适合进入方法前的权限验证, @PreAuthorize 可以将登录用 户的 roles/permissions 参数传到方法中
在启动类或者配置类先开启注解功能:
@EnableGlobalMethodSecurity(prePostEnabled = true)
在controller方法上添加注解
@RequestMapping("/update")
@PreAuthorize("hasAnyAuthority('admin')")
public String update()
{
return "update";
}
UserDetailsService设置用户角色
List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_manager,admin");
return new User(users.getUsername(),
new BCryptPasswordEncoder().encode(users.getPassword()),auths);
@PostAuthorize
在启动类或者配置类先开启注解功能:
@EnableGlobalMethodSecurity(prePostEnabled = true)
@PostAuthorize 注解使用并不多,在方法执行后再进行权限验证,适合验证带有返回值的权限
在controller方法上添加注解
@RequestMapping("/delete")
@PostAuthorize("hasAnyAuthority('teacher')")
public String delete(){
System.out.println("delete 已经执行");
return "delete";
}
UserDetailsService设置用户角色
List<GrantedAuthority> auths= AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_manager,admin");
return new User(users.getUsername(),
new BCryptPasswordEncoder().encode(users.getPassword()),auths);
可以一眼就看出来是没有权限访问该路径的 但是里面打印的内容却已经执行了


@PostFilter
@PostFilter :权限验证之后对数据进行过滤 留下用户名是 admin1 的数据表达式中的 filterObject 引用的是方法返回值 List 中的某一个元素
对方法的返回数据过滤
@RequestMapping("getAll")
@PreAuthorize("hasRole('ROLE_管理员')")
@PostFilter("filterObject.username == 'admin1'")
@ResponseBody
public List<UserInfo> getAllUser(){
ArrayList<UserInfo> list = new ArrayList<>();
list.add(new UserInfo(1l,"admin1","6666"));
list.add(new UserInfo(2l,"admin2","888"));
return list;
}
只有username是admin1的才会返回
@PreFilter
@PreFilter: 进入控制器之前对数据进行过滤
@RequestMapping("getTestPreFilter")
@PreAuthorize("hasRole('ROLE_管理员')")
@PreFilter(value = "filterObject.id%2==0")
@ResponseBody
public List<UserInfo> getTestPreFilter(@RequestBody List<UserInfo>
list){
list.forEach(t-> {
System.out.println(t.getId()+"\t"+t.getUsername());
});
return list;
}
只有id是偶数的才会传进去
用户注销
success.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录成功</h1>
<a href="/logout">退出</a>
</body>
</html>
在配置类中添加退出映射地址
http.logout().logoutUrl("/logout").logoutSuccessUrl("/index").permitAll();
退出之后,是无法访问需要登录时才能访问的控制器
基于数据库的记住我
数据库表
CREATE TABLE `persistent_logins` (
`username` varchar(64) NOT NULL,
`series` varchar(64) NOT NULL,
`token` varchar(64) NOT NULL,
`last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE
CURRENT_TIMESTAMP,
PRIMARY KEY (`series`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
添加数据库的配置文件
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: 123456
thymeleaf:
cache: false
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
编写配置类
要注入数据源和PersistentTokenRepository实现类
@Autowired
private DataSource dataSource;
@Bean
public PersistentTokenRepository persistentTokenRepository(){
JdbcTokenRepositoryImpl jdbcTokenRepository=new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
return jdbcTokenRepository;
}
.and().rememberMe().tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(60)
.userDetailsService(userDetailsService)
package com.blb.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.sql.DataSource;
@Configuration
public class SecurityConfig5 extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Bean
public PersistentTokenRepository persistentTokenRepository(){
JdbcTokenRepositoryImpl jdbcTokenRepository=new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
return jdbcTokenRepository;
}
@Bean
BCryptPasswordEncoder password(){
return new BCryptPasswordEncoder();
}
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(password());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling().accessDeniedPage("/403.html");
http.logout().logoutUrl("/logout").logoutSuccessUrl("/index").permitAll();
http.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/user/login")
.defaultSuccessUrl("/success.html").permitAll()
.and().authorizeRequests()
.antMatchers("/","/hello","/user/login").permitAll()
.anyRequest().authenticated()
.and().rememberMe().tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(60)
.userDetailsService(userDetailsService)
.and().csrf().disable();
}
}
页面添加记住我复选框
记住我:<input type="checkbox"name="remember-me"title="记住密码"/><br/>
此处:name 属性值必须位 remember-me.不能改为其他值
结合thymeleaf
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
- 整合包4(springsecurity4)——springboot版本2.0.9
- 整合包5(springsecurity5)——springboot版本之后
导入命名空间
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<div class="right menu">
<div sec:authorize="!isAuthenticated()">
<a class="item" th:href="@{/login}">
<i class="address card icon"></i> 登录
</a>
</div>
<div sec:authorize="isAuthenticated()">
<a class="item">
<i class="address card icon"></i>
用户名:<span sec:authentication="principal.username"></span>
角色:<span sec:authentication="principal.authorities"></span>
</a>
</div>
<div sec:authorize="isAuthenticated()">
<a class="item" th:href="@{/logout}">
<i class="sign-out icon"></i> 注销
</a>
</div>
</div>
<div class="column" sec:authorize="hasRole('vip1')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 1</h5>
<hr>
<div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
<div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
<div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
</div>
</div>
</div>
</div>
<div class="column" sec:authorize="hasRole('vip2')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 2</h5>
<hr>
<div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
<div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
<div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
</div>
</div>
</div>
</div>
<div class="column" sec:authorize="hasRole('vip3')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 3</h5>
<hr>
<div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
<div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
<div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
</div>
</div>
</div>
</div>
|