此示例是使用Spring Security实现登录功能。
thymeleaf-extras-springsecurity5是springsecurity针对thymeleaf模板引擎而开发的一套标签库,可以在thymeleaf中引入该标签库,然后就可以在thymeleaf中使用springsecurity提供的标签了。
注意:Spring Boot 2.x 要用thymeleaf-extras-springsecurity5; Spring Boot 1.x用thymeleaf-extras-springsecurity4
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.4.RELEASEversion>
<relativePath/>
parent>
<groupId>com.examplegroupId>
<artifactId>spring-security-exampleartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>spring-security-examplename>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
<dependency>
<groupId>org.thymeleaf.extrasgroupId>
<artifactId>thymeleaf-extras-springsecurity5artifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.6version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
login.html
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<title>登录title>
head>
<body>
<form method="post" action="/login">
<h2 class="form-signin-heading">登录h2>
<span th:if="${param.error}" th:text="${session.SPRING_SECURITY_LAST_EXCEPTION.message}">span>
<p>
<label for="username">用户名label>
<input type="text" id="username" name="username" required autofocus>
p>
<p>
<label for="password">密码label>
<input type="password" id="password" name="password" required>
p>
<button type="submit">登录button>
form>
body>
html>
index.html
引入标签库xmlns:sec=“http://www.thymeleaf.org/thymeleaf-extras-springsecurity4”,引入标签库这里使用thymeleaf-extras-springsecurity4,标签库就是对应于org.springframework.security.access.expression.SecurityExpressionRoot这个类。
常用的标签库
SecurityExpressionRoot常用的表达式
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta charset="utf-8">
head>
<body>
<div sec:authorize="isAuthenticated()">
<p>登录名:<span sec:authentication="name">span>p>
<p>角色:<span sec:authentication="principal.authorities">span>p>
<p>Username:<span sec:authentication="principal.username">span>p>
<div sec:authorize="hasAuthority('ROLE_user:view:GET')">用户列表div>
<div sec:authorize="hasRole('user:add:POST')">添加用户div>
<div sec:authorize="hasAuthority('ROLE_user:update:PUT')">修改用户div>
<div sec:authorize="hasAuthority('ROLE_user:delete:DELETE')">删除用户div>
div>
body>
html>
userList.html
<html lang="en">
<head>
<meta charset="utf-8">
head>
<body>
用户列表
body>
html>
orderList.html
<html lang="en">
<head>
<meta charset="utf-8">
head>
<body>
订单列表
body>
html>
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/index").setViewName("index");
registry.addViewController("/getUserList").setViewName("userList");
registry.addViewController("/getOrderList").setViewName("orderList");
}
}
权限实体实现GrantedAuthority接口,重写getAuthority方法,该方法唯一标识一个权限
@Data
@ToString
@AllArgsConstructor
@RequiredArgsConstructor
public class SysPermission implements GrantedAuthority {
private Long id;
private String name;
private String code;
private String url;
private String method;
@Override
public String getAuthority() {
return "ROLE_" + this.code + ":" + this.method.toUpperCase();
}
}
@Data
@ToString
@AllArgsConstructor
@RequiredArgsConstructor
public class SysUser {
private Long id;
private String username;
private String password;
private List<SysPermission> sysPermissions;
}
从数据库中获取用户的信息和权限列表
@Component
public class MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 模拟根据用户名查询用户信息和权限
SysUser user = new SysUser(1L, "admin", "$2a$10$nm5H9QvnoWao.l7NbxQGZeZoR0Cn.VqCpsl3E/FhglPa954Zg9ccm", Arrays.asList(
new SysPermission(1L, "用户列表", "user:view", "/getUserList", "GET"),
new SysPermission(2L, "添加用户", "user:add", "/addUser", "POST"),
new SysPermission(3L, "修改用户", "user:update", "/updateUser", "PUT")
));
if (user == null) {
throw new UsernameNotFoundException(username);
}
return new User(user.getUsername(), user.getPassword(), user.getSysPermissions());
}
public static void main(String[] args) {
System.out.println(new BCryptPasswordEncoder().encode("123456"));
}
}
登录成功后处理的逻辑,默认是重定向到上一个url,也可以在这里自定义自己的逻辑。
@Slf4j
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
log.info("login sucesssful {}", objectMapper.writeValueAsString(authentication));
response.sendRedirect("/index");
}
}
Spring Security的核心配置,注意BCryptPasswordEncoder是用于加密解密密码的,每次加密的密文都不一样。
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetailsService myUserDetailsService;
@Autowired
private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
// 配置需要认证的请求
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
// 登录表单相关配置
.formLogin()
.loginPage("/login")
.usernameParameter("username")
.passwordParameter("password")
.successHandler(myAuthenticationSuccessHandler)
.failureUrl("/login?error")
.permitAll()
.and()
// 登出相关配置
.logout()
.permitAll();
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder());
}
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/static/**");
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}