原文地址:https://liuyanzhao.com/7431.html
本文通过一个登录的例子介绍 SpringBoot + Spring Security + Thymeleaf 权限管理。
一、数据库
用户登录账号是 admin,saysky,lockeduser
密码都是 123456
1、表结构
user 表
authority 表
user_authority 表
2、数据
user 表
authority 表
user_authority 表
3、SQL 代码
SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `authority`;
CREATE TABLE `authority` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
BEGIN;
INSERT INTO `authority` VALUES ('1', 'ROLE_ADMIN'), ('2', 'ROLE_USER');
COMMIT;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(20) NOT NULL,
`password` varchar(100) NOT NULL,
`name` varchar(20) NOT NULL,
`email` varchar(50) NOT NULL,
`avatar` varchar(200) DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`last_login_time` datetime DEFAULT NULL,
`status` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UK_ob8kqyqqgmefl0aco34akdtpe` (`email`),
UNIQUE KEY `UK_sb8bbouer5wak8vyiiy4pf2bx` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
BEGIN;
INSERT INTO `user` VALUES ('1', 'admin', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', '管理员', '[email protected]', null, null, null, 'normal'), ('2', 'saysky', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', '言曌', '[email protected]', null, null, null, 'normal'), ('3', 'lockuser', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', '锁定账号', '[email protected]', null, null, null, 'locked');
COMMIT;
DROP TABLE IF EXISTS `user_authority`;
CREATE TABLE `user_authority` (
`user_id` bigint(20) NOT NULL,
`authority_id` bigint(20) NOT NULL,
KEY `FKgvxjs381k6f48d5d2yi11uh89` (`authority_id`),
KEY `FKpqlsjpkybgos9w2svcri7j8xy` (`user_id`),
CONSTRAINT `FKgvxjs381k6f48d5d2yi11uh89` FOREIGN KEY (`authority_id`) REFERENCES `authority` (`id`),
CONSTRAINT `FKpqlsjpkybgos9w2svcri7j8xy` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
BEGIN;
INSERT INTO `user_authority` VALUES ('1', '1'), ('2', '2'), ('1', '2');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
二、Maven 依赖
pom.xml
主要需要 SpringBoot、Thymeleaf、Spring Security 的依赖
三、实体类
User.java
- package com.liuyanzhao.chuyun.entity;
- import lombok.Data;
- import org.hibernate.validator.constraints.Email;
- import org.hibernate.validator.constraints.NotEmpty;
- import org.springframework.security.core.GrantedAuthority;
- import org.springframework.security.core.authority.SimpleGrantedAuthority;
- import org.springframework.security.core.userdetails.UserDetails;
- import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
- import org.springframework.security.crypto.password.PasswordEncoder;
- import javax.persistence.;
- import javax.validation.constraints.Size;
- import java.io.Serializable;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Date;
- import java.util.List;
- @Entity
- @Data
- public class User implements UserDetails, Serializable {
- private static final long serialVersionUID = 1L;
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
- @NotEmpty(message = “昵称不能为空”)
- @Size(min=2, max=20)
- @Column(nullable = false, length = 20)
- private String name;
- @NotEmpty(message = “邮箱不能为空”)
- @Size(max=50)
- @Email(message= “邮箱格式不对” )
- @Column(nullable = false, length = 50, unique = true)
- private String email;
- @NotEmpty(message = “账号不能为空”)
- @Size(min=3, max=20)
- @Column(nullable = false, length = 20, unique = true)
- private String username;
- @NotEmpty(message = “密码不能为空”)
- @Size(max=100)
- @Column(length = 100)
- private String password;
- @Column(length = 200)
- private String avatar;
- private Date createTime;
- private Date lastLoginTime;
- @Column(length = 10)
- private String status;
- @ManyToMany(cascade = CascadeType.DETACH, fetch = FetchType.EAGER)
- @JoinTable(name = “user_authority”, joinColumns = @JoinColumn(name = “user_id”, referencedColumnName = “id”),
- inverseJoinColumns = @JoinColumn(name = “authority_id”, referencedColumnName = “id”))
- private List authorities;
- protected User() {
- }
- public User(String name, String email,String username,String password) {
- this.name = name;
- this.email = email;
- this.username = username;
- this.password = password;
- }
- public Collection extends GrantedAuthority> getAuthorities() {
-
- List simpleAuthorities = new ArrayList<>();
- for(GrantedAuthority authority : this.authorities){
- simpleAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority()));
- }
- return simpleAuthorities;
- }
- public void setAuthorities(List authorities) {
- this.authorities = authorities;
- }
- public void setEncodePassword(String password) {
- PasswordEncoder encoder = new BCryptPasswordEncoder();
- String encodePasswd = encoder.encode(password);
- this.password = encodePasswd;
- }
- @Override
- public boolean isAccountNonExpired() {
- return true;
- }
- @Override
- public boolean isAccountNonLocked() {
- return true;
- }
- @Override
- public boolean isCredentialsNonExpired() {
- return true;
- }
- @Override
- public boolean isEnabled() {
- return true;
- }
- }
Authority.java
- package com.liuyanzhao.chuyun.entity;
- import org.springframework.security.core.GrantedAuthority;
- import javax.persistence.;
- @Entity
- public class Authority implements GrantedAuthority {
- private static final long serialVersionUID = 1L;
- @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;
- }
- }
四、Dao 层
UserRepository.java
- package com.liuyanzhao.chuyun.repository;
- import com.liuyanzhao.chuyun.entity.User;
- import org.springframework.data.jpa.repository.JpaRepository;
- public interface UserRepository extends JpaRepository {
-
- User findByUsername(String username);
- }
五、Service 层
CustomUserService.java
- package com.liuyanzhao.chuyun.service;
- import com.liuyanzhao.chuyun.entity.User;
- import com.liuyanzhao.chuyun.repository.UserRepository;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.security.authentication.LockedException;
- import org.springframework.security.core.userdetails.UserDetailsService;
- import org.springframework.security.core.userdetails.UsernameNotFoundException;
- import org.springframework.stereotype.Service;
- @Service
- public class CustomUserService implements UserDetailsService{
- @Autowired
- private UserRepository userRepository;
- @Override
- public User loadUserByUsername(String username) throws UsernameNotFoundException {
- User user = userRepository.findByUsername(username);
- if (user == null) {
- throw new UsernameNotFoundException(“用户名不存在”);
- } else if(“locked”.equals(user.getStatus())) {
- throw new LockedException(“用户被锁定”);
- }
- return user;
- }
- }
六、Spring Security 配置
SecurityConfig.java
- package com.liuyanzhao.chuyun.config;
- import com.liuyanzhao.chuyun.service.CustomUserService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.annotation.Bean;
- import org.springframework.security.authentication.AuthenticationProvider;
- import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
- 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;
- import org.springframework.security.crypto.password.PasswordEncoder;
- @EnableWebSecurity
- @EnableGlobalMethodSecurity(prePostEnabled = true)
- public class SecurityConfig extends WebSecurityConfigurerAdapter {
- private static final String KEY = “liuyanzhao.com”;
- @Autowired
- private PasswordEncoder passwordEncoder;
- @Bean
- CustomUserService customUserService() {
- return new CustomUserService();
- }
- @Bean
- public PasswordEncoder passwordEncoder() {
- return new BCryptPasswordEncoder();
- }
- @Bean
- public AuthenticationProvider authenticationProvider() {
- DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
- authenticationProvider.setUserDetailsService(customUserService());
- authenticationProvider.setPasswordEncoder(passwordEncoder);
- return authenticationProvider;
- }
-
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http.authorizeRequests().antMatchers(“/”,“/css/”, “/js/”, “/fonts/”,“/users”).permitAll()
- .antMatchers(“/h2-console/”).permitAll()
- .antMatchers(“/admin/”).hasRole(“ADMIN”)
- .antMatchers(“/console/”).hasAnyRole(“ADMIN”,“USER”)
- .and()
- .formLogin()
- .loginPage(“/login”).failureUrl(“/login?error=true”)
- .and().rememberMe().key(KEY)
- .and().exceptionHandling().accessDeniedPage(“/403”);
- http.csrf().ignoringAntMatchers(“/h2-console/”);
- http.headers().frameOptions().sameOrigin();
- }
- /*
- 认证信息管理
-
- @param auth
- @throws Exception
- */
- @Autowired
- public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
-
- auth.userDetailsService(customUserService());
- auth.authenticationProvider(authenticationProvider());
- }
- }
七、HTML 页面
首页:http://localhost:8080
登录页面:http://localhost:8080/login
登录错误页面:http://localhost:8080/login?error=true
退出登录:http://localhost:8080/logout
index.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>
- <meta charset=“UTF-8”>
- <meta name=“viewport”
- content=“width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no”>
- head>
- <body>
- <div sec:authorize=“isAnonymous()”>
- 未登录,点击 <a th:href=“@{/login}”>登录a>
- div>
- <div sec:authorize=“isAuthenticated()”>
- <p>已登录p>
- <p>登录名:<span sec:authentication=“name”>span>p>
- <p>角色:<span sec:authentication=“principal.authorities”>span>p>
- <p>Username:<span sec:authentication=“principal.username”>span>p>
- <p>Password:<span sec:authentication=“principal.password”>span>p>
- <p>Email :<span sec:authentication=“principal.email”>span>p>
- <p>Name:<span sec:authentication=“principal.name”>span>p>
- <p>Status:<span sec:authentication=“principal.status”>span>p>
- <p>拥有的角色:
- <span sec:authorize=“hasRole(‘ROLE_ADMIN’)”>管理员span>
- <span sec:authorize=“hasRole(‘ROLE_USER’)”>用户span>
- p>
- div>
- body>
- html>
login.html
- >
- <html xmlns=”http://www.w3.org/1999/xhtml”
- xmlns:th=”http://www.thymeleaf.org”>
- <head>
- <meta http-equiv=“Content-Type” content=“text/html; charset=utf-8”/>
- <meta name=“viewport” content=“width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no”>
- <title>登录页面title>
- <body>
- <form th:action=“@{login}” method=“post” id=“loginForm”>
- <span th:if=“{param.error}" th:text=" {param.error}" th:text=" {session.SPRING_SECURITY_LAST_EXCEPTION.message}”>span>
- 用户名:<input type=“text” name=“username” class=“username” id=“username” placeholder=“用户名” autocomplete=“off”/> <br>
- 密 码:<input type=“password” name=“password” class=“password” id=“password” placeholder=“密码”
- oncontextmenu=“return false”
- onpaste=“return false”/> <br>
- <input type=“checkbox” name=“remember-me”/>记住我 <br>
- <input id=“submit” type=“submit” value=“登录”/>
- form>
- body>
- html>
八、运行效果
1、访问首页:http://localhost:8080