文章目录
快速入门
导入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<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.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
配置WebSecurityConfigurerAdapter
新建SecurityConfig类,作为配置类。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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;
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) // 注解开启了方法级别的保护
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService()).passwordEncoder(new BCryptPasswordEncoder());
}
@Bean
public UserDetailsService userDetailsService() {
// 在内存中存放用户信息
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
// 在内存中创建了 一个认证用户的信息,yq, 密码为123456, 有ROLE_USER的角色。
manager.createUser(
User.withUsername("yq").password(new BCryptPasswordEncoder()
.encode("123456")).roles("USER").build());
manager.createUser(
User.withUsername("admin").password(new BCryptPasswordEncoder()
.encode("123456")).roles("ADMIN").build());
return manager;
}
// 配置如何验证身份
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// 启用基于 HttpServletRequest 的访问限制,开始配置哪些URL需要被保护、哪些不需要被保护
.authorizeRequests()
// 允许未登陆用户访问的资源
.antMatchers( "/favicon.ico","/css/**","/index","/js/**","/images/**").permitAll()
// /user/**的这个资源需要有ROLE_USER的这个角色才能访问,不然就会提示拒绝访问
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/blogs/**").hasRole("ADMIN")
// 任何尚未匹配的URL只需要验证用户即可访问
.anyRequest().authenticated()
.and()
// 设置登陆相关的配置
.formLogin()
// 登陆界面页面跳转URL
.loginPage("/login")
// 登陆失败页面跳转URL
.failureUrl("/login-error")
// 登陆成功跳转的页面,可以不设置,默认跳转到需要登陆之前的页面
// .defaultSuccessUrl("/index")
// permitAll表示不需要验证 登录页面,登录失败页面
.permitAll()
.and()
// 权限拒绝页面跳转URL
.exceptionHandling().accessDeniedPage("/401");
// 注销登录成功,重定向到首页
http.logout().logoutSuccessUrl("/");
}
}
编写控制器
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MainController {
@RequestMapping("/")
public String root() {
return "redirect:/index";
}
@RequestMapping("/index")
public String index() {
return "index";
}
@RequestMapping("/user/index")
public String userIndex() {
return "user/index";
}
@RequestMapping("/login")
public String login() {
return "login";
}
@RequestMapping("/login-error")
public String loginError(Model model) {
model.addAttribute("loginError", true);
return "login";
}
@GetMapping("/401")
public String accesssDenied() {
return "401";
}
}
配置文件application.yml
spring:
thymeleaf:
mode: HTML5
encoding: UTF-8
cache: false
编写相关界面
登录界面login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login page</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
</head>
<body>
<h1>Login page</h1>
<p>User角色用户: yq / 123456</p>
<p>Admin角色用户: admin / 123456</p>
<p th:if="${loginError}" class="error">用户名或密码错误</p>
<form th:action="@{/login}" method="post">
<label for="username">用户名</label>:
<input type="text" id="username" name="username" autofocus="autofocus" /> <br />
<label for="password">密码</label>:
<input type="password" id="password" name="password" /> <br />
<input type="submit" value="登录" />
</form>
<p><a href="/index" th:href="@{/index}">返回首页</a></p>
</body>
</html>
首页index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<title>Hello Spring Security</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
</head>
<body>
<h1>Hello Spring Security</h1>
<p>这个界面没有受保护,你可以进已被保护的界面.</p>
<div th:fragment="logout" sec:authorize="isAuthenticated()">
登录用户: <span sec:authentication="name"></span> |
用户角色: <span sec:authentication="principal.authorities"></span>
<div>
<form action="#" th:action="@{/logout}" method="post">
<input type="submit" value="登出" />
</form>
</div>
</div>
<ul>
<li>点击<a href="/user/index" th:href="@{/user/index}">去/user/index保护的界面</a></li>
</ul>
</body>
</html>
权限不够显示的界面401.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"
xmlns:th="http://www.thymeleaf.org">
<body>
<div >
<div th:substituteby="index::logout"></div>
<div >
<h2> 权限不够</h2>
</div>
<div sec:authorize="isAuthenticated()">
<p>已有用户登录</p>
<p>用户: <span sec:authentication="name"></span></p>
<p>角色: <span sec:authentication="principal.authorities"></span></p>
</div>
<div sec:authorize="isAnonymous()">
<p>未有用户登录</p>
</div>
<p>
拒绝访问!
</p>
</div>
</body>
</html>
用户首页/user/index.html
界面,该资源被Spring Security保护,只有拥有ROLE_USER
角色的用户才能够访问。
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Hello Spring Security</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="/css/main.css" th:href="@{/css/main.css}" />
</head>
<body>
<div th:substituteby="index::logout"></div>
<h1>这个界面是被保护的界面</h1>
<p><a href="/index" th:href="@{/index}">返回首页</a></p>
<p><a href="/blogs" th:href="@{/blogs}">管理博客</a></p>
</body>
</html>
main.css
body {
font-family: sans;
font-size: 1em;
}
p.error {
font-weight: bold;
color: red;
}
div.logout {
float: right;
}
启动测试
http://127.0.0.1:8080/index
单击/user/index保护的界面
,由于/user/index
界面需要ROLE_USER
权限,由于未登录,会被重定向到登录界面/login.html
。
用具有ROLE_USER
权限角色的用户登录,用户名:yq ,密码:123456。登录成功,界面会被重定向到http://localhost:8080/user/index界面,注意该界面是具有ROLE_USER
权限角色的用户才具有访问权限。
然后登出yq用户,用具有ROLE_ADMIN
权限角色的用户登录,会被重定向到权限不足的页面。
方法级别的安全支持
@EnableGlobalMethodSecurity(prePostEnabled = true)
注解可以开启方法级别的保护
prePostEnabled
:Spring Security 的 Pre 和 Post 注解是否可用。@PreAuthorize
注解会在进入方法前进行权限验证@PostAuthorize
注解在方法执行后再进行权限验证
具有ROLE_ADMIN
角色的用户才能访问
@PreAuthorize(“hasAuthority(‘ROLE_ADMIN’)”)
也可以写为
@PreAuthorize(“hasRole(‘ROLE_ADMIN’)”)
这二者是等价的。
加多个权限角色
@PreAuthorize(“hasAnyRole(‘ROLE_ADMIN’,‘ROLE_USER’)”)
也可以写为
@PreAuthorize(“hasAnyAuthority(‘ROLE_ADMIN’,‘ROLE_USER’)”)
这二者是等价的。
案例编写
1、实体
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Blog {
private Long id;
private String name;
private String content;
}
2、编写Service,模拟一个Dao
import com.example.security.entity.Blog;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@Service
public class BlogService {
private List<Blog> list=new ArrayList<>();
public BlogService(){
list.add(new Blog(1L,"spring in action", "good!"));
list.add(new Blog(2L,"spring cloud in action", "nice!"));
list.add(new Blog(3L,"Hadoop in action", "nice!"));
list.add(new Blog(4L,"Yarn in action", "nice!"));
list.add(new Blog(5L,"Java in action", "nice!"));
}
public List<Blog> getBlogs() {
return list;
}
public void deleteBlog(long id) {
Iterator iter = list.iterator();
while(iter.hasNext()) {
Blog blog= (Blog) iter.next();
if (blog.getId()==id){
iter.remove();
}
}
}
}
3、编写Controller
只有ROLE_ADMIN
角色的用户才能删除一条数据。
import com.example.security.entity.Blog;
import com.example.security.service.BlogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;
@RestController
@RequestMapping("/blogs")
public class BlogController {
@Autowired
BlogService blogService;
@GetMapping
public ModelAndView list(Model model) {
List<Blog> list =blogService.getBlogs();
model.addAttribute("blogsList", list);
return new ModelAndView("blogs/list", "blogModel", model);
}
@PreAuthorize("hasAnyAuthority('ROLE_ADMIN')")
@GetMapping(value = "/{id}/deletion")
public ModelAndView delete(@PathVariable("id") Long id, Model model) {
blogService.deleteBlog(id);
model.addAttribute("blogsList", blogService.getBlogs());
return new ModelAndView("blogs/list", "blogModel", model);
}
}
博客清单页面/blogs/list.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div>
<table >
<thead>
<tr>
<td>博客编号</td>
<td>博客名称</td>
<td>博客描述</td>
</tr>
</thead>
<tbody>
<tr th:each="blog: ${blogModel.blogsList}">
<td th:text="${blog.id}"></td>
<td th:text="${blog.name}"></td>
<td th:text="${blog.content}"></td>
<td>
<div >
<a th:href="@{'/blogs/' + ${blog.id}+'/deletion'}">
删除
</a>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
4、启动测试
使用yq用户登陆,可以访问博客清单页面,删除会提示权限不足。
使用admin用户登陆,可以正常删除博客。
从数据库中读取用户的认证信息
在快速入门中,采用了从内存中配置用户信息,包括用户名、密码、用户的角色权限信息。如果用户数量非常多时,这种方式显然是不可行的 。
下面编写如何从数据库中读取用户和用户的角色权限信息,采用的数据库为MySQL,ORM 框架为 JPA 。
1、导入依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.42</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
2、配置文件application.yml
spring:
thymeleaf:
mode: HTML5
encoding: UTF-8
cache: false
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
username: root
password: 123456
jpa:
hibernate:
ddl-auto: update
show-sql: true
3、创建实体
User 类实现了 UserDetails 接口,该接口是实现 Spring Security 认证信息的核心接口。其中,getUsemame()
方法为 UserDetails 接口的方法,这个方法不一定返回usemame ,也可以是其他的用户信息,例如:手机号码、邮箱地址等。 getAuthorities()
方法返回的是该用设置的权限信息,在本例中,从数据库中读取该用户的所有角色信息,权限信息也可以是用户的其他信息,不一定是角色信息。另外需要读取密码,最后几个方法一般情况下都返回 true ,也可以根据自己的需求进行业务判断。
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
@Entity
public class User implements UserDetails, Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column
private String password;
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
private List<Role> authorities;
public User() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
public void setAuthorities(List<Role> authorities) {
this.authorities = authorities;
}
@Override
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
Role 类实现了 GrantedAuthority 接口,并重写了其 getAuthority()
方法 。权限点可 以为任何字符串,不一定是角色名的字符串,关键是 getAuthority()
方法如何实现。本例的权限点是从数据库读取 Role 表 的 name 字段。
import org.springframework.security.core.GrantedAuthority;
import javax.persistence.*;
@Entity
public class Role implements GrantedAuthority {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public String getAuthority() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
4、创建Dao
UserDao 中自定义一个根据 usemame 获取 User 的方法,由于 JPA 已经自动实现了根据 usemame 字段去查找用户的方法,因此不需要额外的工作。
import com.example.security.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserDao extends JpaRepository<User, Long>{
User findByUsername(String username);
}
5、创建Service
Service 层需要实现 UserDetailsService 接口。
import com.example.security.dao.UserDao;
import com.example.security.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class UserService implements UserDetailsService {
@Autowired
private UserDao userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User userDetails = userRepository.findByUsername(username);
return userDetails;
}
}
6、修改SecurityConfig配置类
import com.example.security.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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;
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig2 extends WebSecurityConfigurerAdapter {
@Autowired
UserService userService;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
}
// 配置如何验证身份代码省略,参考快速入门
}
7、数据库表准备
启动程序后,JPA 会连接数据库,在数据库自动建表。
数据库建表脚本如下:
CREATE TABLE `role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `role` VALUES (1, 'ROLE_USER');
INSERT INTO `role` VALUES (2, 'ROLE_ADMIN');
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `UK_sb8bbouer5wak8vyiiy4pf2bx`(`username`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- 密码经过BCryptPasswordEncoder进行加密,解密后是:123456
INSERT INTO `user` VALUES (1, '$2a$10$Ce7vMjN1T/X.nlSAVTxDjeGTAuPdOGxEpMNDCqNwjeJv.BORvgXGS', 'yq');
INSERT INTO `user` VALUES (2, '$2a$10$Ce7vMjN1T/X.nlSAVTxDjeGTAuPdOGxEpMNDCqNwjeJv.BORvgXGS', 'admin');
CREATE TABLE `user_role` (
`user_id` bigint(20) NOT NULL,
`role_id` bigint(20) NOT NULL,
INDEX `FKa68196081fvovjhkek5m97n3y`(`role_id`) USING BTREE,
INDEX `FK859n2jvi8ivhui0rl0esws6o`(`user_id`) USING BTREE
) ENGINE = MyISAM CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Fixed;
INSERT INTO `user_role` VALUES (1, 1);
INSERT INTO `user_role` VALUES (2, 2);
8、启动测试
和快速入门的效果是一样的。
Session超时处理
设置为10秒(不设置默认是半小时),即连续10秒不操作就会失效。
server:
servlet:
session:
timeout: 10
等10秒之后,任然可以访问,Session并没有失效。
查看TomcatServletWebServerFactory
类源码中设置Session的方法,发现如果你设置的Session过期时间是小于1分钟的,那就是1分钟。
获取用户信息
通过Bean获取用户
获取当前认证用户(authenticated principal)最简单的方式是通过SecurityContextHolder
类的静态方法:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// 首先检查是否存在认证用户
if (!(authentication instanceof AnonymousAuthenticationToken)) {
String currentUserName = authentication.getName();
return currentUserName;
}
在控制器中获取用户
在@Controller 注解的bean里,有额外的选项。Principal 和 Authentication 可以直接作为方法参数,框架会自动赋值。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.security.Principal;
@Controller
public class SecurityController {
@GetMapping("/getName")
@ResponseBody
public String getName(Principal principal){
return principal.getName();
}
}
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class SecurityController {
@GetMapping("/getName")
@ResponseBody
public String getName(Authentication authentication){
return authentication.getName();
}
}
框架为Authentication
类赋予了很多的功能。
通过HttpRequest:
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
@Controller
public class SecurityController {
@GetMapping("/getName")
@ResponseBody
public String getName(HttpServletRequest request){
Principal principal = request.getUserPrincipal();
return principal.getName();
}
}
通过自定义接口获取用户信息
为了充分利用spring的依赖注入功能,可以在在任何地方获取认证信息,不仅仅是@Controller注解的bean,可以通过简单的门面隐藏静态访问。
import org.springframework.security.core.Authentication;
// 门面接口
public interface IAuthenticationFacade {
Authentication getAuthentication();
}
//
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
// 门面实现类
@Component
public class AuthenticationFacade implements IAuthenticationFacade {
@Override
public Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
}
//
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class SecurityController {
@Autowired
private IAuthenticationFacade authenticationFacade;
@GetMapping("/getName")
@ResponseBody
public String getName(){
Authentication authentication = authenticationFacade.getAuthentication();
return authentication.getName();
}
在JSP中获取用户信息
当前认证用户也可以在jsp页面中获取到。利用spring security标签支持。需要在页面中定义标签:
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
然后可以引用principal:
<security:authorize access="isAuthenticated()">
authenticated as <security:authentication property="principal.username" />
</security:authorize>
在thymeleaf中获取用户信息
可以通过security为thymeleaf增加的扩增标签来实现。
需要导入依赖。
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
需要引入sec标签。
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
通过sec标签获取用户信息。
<div th:fragment="logout" sec:authorize="isAuthenticated()">
登录用户: <span sec:authentication="name"></span> |
用户角色: <span sec:authentication="principal.authorities"></span>
<div>
<form action="#" th:action="@{/logout}" method="post">
<input type="submit" value="登出" />
</form>
</div>
</div>
使用总结
- 引入 Spring Security 相关的依赖。
- 编写一个配置类,该配置类继承了 WebSecurityConfigurerAdapter ,并在该配置类上加@EnableWebSecurity 注解开启 Web Security。
- 通过 AuthenticationManagerBuilder 配置读取用户的认证信息的方式,可以从内存中读取,也可以从数据库中读取。
- 通过 HttpSecurity 配置请求的认证规则,即哪些 URI 请求需要认证、哪些不需要 ,以及需要拥有什么权限才能访问。
- 通过 @EnableGlobalMethodSecurity(prePostEnabled = true) 注解开启方法级别的安全配置,方法级别上的安全控制支持 secureEnabled、jsr250Enabled、
prePostEnabled
3种方式,用的最多的是 prePostEnabled。其中,prePostEnabled 包括PreAuthorize
和PostAuthorize
两种形式,一般只用到 PreAuthorize 这种方式 。
参考:
SpringBoot2.x整合Security5(完美解决 There is no PasswordEncoder mapped for the id “null”)
Spring Security 无法登陆,报错:There is no PasswordEncoder mapped for the id “null”