(转载)SpringBoot + Spring Security + Thymeleaf 实现权限管理登录

原文地址:https://liuyanzhao.com/7431.html


本文通过一个登录的例子介绍 SpringBoot + Spring Security + Thymeleaf 权限管理。

一、数据库

用户登录账号是 admin,saysky,lockeduser

密码都是 123456

1、表结构

user 表

SpringBoot + Spring Security + Thymeleaf 实现权限管理登录

authority 表

SpringBoot + Spring Security + Thymeleaf 实现权限管理登录

user_authority 表

SpringBoot + Spring Security + Thymeleaf 实现权限管理登录

2、数据

user 表

SpringBoot + Spring Security + Thymeleaf 实现权限管理登录

authority 表

SpringBoot + Spring Security + Thymeleaf 实现权限管理登录

user_authority 表

SpringBoot + Spring Security + Thymeleaf 实现权限管理登录

3、SQL 代码

SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
--  Table structure for `authority`
-- ----------------------------
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;
-- ----------------------------
--  Records of `authority`
-- ----------------------------
BEGIN;
INSERT INTO `authority` VALUES ('1', 'ROLE_ADMIN'), ('2', 'ROLE_USER');
COMMIT;
-- ----------------------------
--  Table structure for `user`
-- ----------------------------
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;
-- ----------------------------
--  Records of `user`
-- ----------------------------
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;
-- ----------------------------
--  Table structure for `user_authority`
-- ----------------------------
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;
-- ----------------------------
--  Records of `user_authority`
-- ----------------------------
BEGIN;
INSERT INTO `user_authority` VALUES ('1', '1'), ('2', '2'), ('1', '2');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;

二、Maven 依赖

pom.xml

  1. <?xml version=“1.0” encoding=“UTF-8”?>
  2. <project xmlns=http://maven.apache.org/POM/4.0.0 xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
  3.          xsi:schemaLocation=http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd>
  4.     <modelVersion>4.0.0</modelVersion>
  5.     <groupId>com.liuyanzhao</groupId>
  6.     <artifactId>chuyun</artifactId>
  7.     <version>0.0.1-SNAPSHOT</version>
  8.     <packaging>war</packaging>
  9.     <name>chuyun</name>
  10.     <description>Chuyun Blog for Spring Boot</description>
  11.     <parent>
  12.         <groupId>org.springframework.boot</groupId>
  13.         <artifactId>spring-boot-starter-parent</artifactId>
  14.         <version>1.5.9.RELEASE</version>
  15.         <relativePath/> <!– lookup parent from repository –>
  16.     </parent>
  17.     <properties>
  18.         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  19.         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  20.         <java.version>1.8</java.version>
  21.         <thymeleaf.version>3.0.3.RELEASE</thymeleaf.version>
  22.         <thymeleaf-layout-dialect.version>2.0.0</thymeleaf-layout-dialect.version>
  23.         <elasticsearch.version>2.4.4</elasticsearch.version>
  24.     </properties>
  25.     <dependencies>
  26.         <dependency>
  27.             <groupId>org.springframework.boot</groupId>
  28.             <artifactId>spring-boot-starter-web</artifactId>
  29.         </dependency>
  30.         <!–lombok–>
  31.         <dependency>
  32.             <groupId>org.projectlombok</groupId>
  33.             <artifactId>lombok</artifactId>
  34.             <optional>true</optional>
  35.         </dependency>
  36.         <dependency>
  37.             <groupId>org.springframework.boot</groupId>
  38.             <artifactId>spring-boot-starter-test</artifactId>
  39.             <scope>test</scope>
  40.         </dependency>
  41.         <!– Thymeleaf –>
  42.         <dependency>
  43.             <groupId>org.springframework.boot</groupId>
  44.             <artifactId>spring-boot-starter-thymeleaf</artifactId>
  45.         </dependency>
  46.         <!– Spring Data JPA–>
  47.         <dependency>
  48.             <groupId>org.springframework.boot</groupId>
  49.             <artifactId>spring-boot-starter-data-jpa</artifactId>
  50.         </dependency>
  51.         <!– mysql–>
  52.         <dependency>
  53.             <groupId>mysql</groupId>
  54.             <artifactId>mysql-connector-java</artifactId>
  55.         </dependency>
  56.         <!– 热部署 –>
  57.         <dependency>
  58.             <groupId>org.springframework.boot</groupId>
  59.             <artifactId>spring-boot-devtools</artifactId>
  60.             <optional>true</optional>
  61.         </dependency>
  62.         <!– Spring Boot Elasticsearch 依赖 –>
  63.         <dependency>
  64.             <groupId>org.springframework.boot</groupId>
  65.             <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
  66.         </dependency>
  67.         <dependency>
  68.             <groupId>net.java.dev.jna</groupId>
  69.             <artifactId>jna</artifactId>
  70.             <version>4.3.0</version>
  71.         </dependency>
  72.         <!– Spring Security 依赖 –>
  73.         <dependency>
  74.             <groupId>org.springframework.boot</groupId>
  75.             <artifactId>spring-boot-starter-security</artifactId>
  76.         </dependency>
  77.         <dependency>
  78.             <groupId>org.thymeleaf.extras</groupId>
  79.             <artifactId>thymeleaf-extras-springsecurity4</artifactId>
  80.             <version>3.0.2.RELEASE</version>
  81.         </dependency>
  82.         <!–fasthson–>
  83.         <dependency>
  84.             <groupId>com.alibaba</groupId>
  85.             <artifactId>fastjson</artifactId>
  86.             <version>1.2.7</version>
  87.         </dependency>
  88.         <!–验证码kaptcha–>
  89.         <dependency>
  90.             <groupId>com.github.penggle</groupId>
  91.             <artifactId>kaptcha</artifactId>
  92.             <version>2.3.2</version>
  93.         </dependency>
  94.     </dependencies>
  95.     <build>
  96.         <plugins>
  97.             <plugin>
  98.                 <groupId>org.springframework.boot</groupId>
  99.                 <artifactId>spring-boot-maven-plugin</artifactId>
  100.                 <configuration>
  101.                     <fork>true</fork>
  102.                 </configuration>
  103.             </plugin>
  104.         </plugins>
  105.     </build>
  106. </project>

主要需要 SpringBoot、Thymeleaf、Spring Security 的依赖

三、实体类

User.java

  1. package com.liuyanzhao.chuyun.entity;
  2. import lombok.Data;
  3. import org.hibernate.validator.constraints.Email;
  4. import org.hibernate.validator.constraints.NotEmpty;
  5. import org.springframework.security.core.GrantedAuthority;
  6. import org.springframework.security.core.authority.SimpleGrantedAuthority;
  7. import org.springframework.security.core.userdetails.UserDetails;
  8. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  9. import org.springframework.security.crypto.password.PasswordEncoder;
  10. import javax.persistence.;
  11. import javax.validation.constraints.Size;
  12. import java.io.Serializable;
  13. import java.util.ArrayList;
  14. import java.util.Collection;
  15. import java.util.Date;
  16. import java.util.List;
  17. /
  18.   @author 言曌
  19.   @date 2017/12/28 上午9:06
  20.  /
  21. @Entity // 实体
  22. @Data
  23. public class User implements UserDetails, Serializable {
  24.     private static final long serialVersionUID = 1L;
  25.     @Id // 主键
  26.     @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增长策略
  27.     private Long id; // 用户的唯一标识
  28.     @NotEmpty(message = “昵称不能为空”)
  29.     @Size(min=2, max=20)
  30.     @Column(nullable = false, length = 20// 映射为字段,值不能为空
  31.     private String name;
  32.     @NotEmpty(message = “邮箱不能为空”)
  33.     @Size(max=50)
  34.     @Email(message= “邮箱格式不对” )
  35.     @Column(nullable = false, length = 50, unique = true)
  36.     private String email;
  37.     @NotEmpty(message = “账号不能为空”)
  38.     @Size(min=3, max=20)
  39.     @Column(nullable = false, length = 20, unique = true)
  40.     private String username; // 用户账号,用户登录时的唯一标识
  41.     @NotEmpty(message = “密码不能为空”)
  42.     @Size(max=100)
  43.     @Column(length = 100)
  44.     private String password; // 登录时密码
  45.     @Column(length = 200)
  46.     private String avatar; // 头像图片地址
  47.     private Date createTime;
  48.     private Date lastLoginTime;
  49.     @Column(length = 10)
  50.     private String status;
  51.     @ManyToMany(cascade = CascadeType.DETACH, fetch = FetchType.EAGER)
  52.     @JoinTable(name = “user_authority”, joinColumns = @JoinColumn(name = “user_id”, referencedColumnName = “id”),
  53.             inverseJoinColumns = @JoinColumn(name = “authority_id”, referencedColumnName = “id”))
  54.     private List<Authority> authorities;
  55.     protected User() { // JPA 的规范要求无参构造函数;设为 protected 防止直接使用
  56.     }
  57.     public User(String name, String email,String username,String password) {
  58.         this.name = name;
  59.         this.email = email;
  60.         this.username = username;
  61.         this.password = password;
  62.     }
  63.     public Collection<? extends GrantedAuthority> getAuthorities() {
  64.         //  需将 List<Authority> 转成 List<SimpleGrantedAuthority>,否则前端拿不到角色列表名称
  65.         List<SimpleGrantedAuthority> simpleAuthorities = new ArrayList<>();
  66.         for(GrantedAuthority authority : this.authorities){
  67.             simpleAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority()));
  68.         }
  69.         return simpleAuthorities;
  70.     }
  71.     public void setAuthorities(List<Authority> authorities) {
  72.         this.authorities = authorities;
  73.     }
  74.     public void setEncodePassword(String password) {
  75.         PasswordEncoder  encoder = new BCryptPasswordEncoder();
  76.         String encodePasswd = encoder.encode(password);
  77.         this.password = encodePasswd;
  78.     }
  79.     @Override
  80.     public boolean isAccountNonExpired() {
  81.         return true;
  82.     }
  83.     @Override
  84.     public boolean isAccountNonLocked() {
  85.         return true;
  86.     }
  87.     @Override
  88.     public boolean isCredentialsNonExpired() {
  89.         return true;
  90.     }
  91.     @Override
  92.     public boolean isEnabled() {
  93.         return true;
  94.     }
  95. }

Authority.java

  1. package com.liuyanzhao.chuyun.entity;
  2. import org.springframework.security.core.GrantedAuthority;
  3. import javax.persistence.;
  4. /
  5.   权限
  6.   @author 言曌
  7.   @date 2018/1/26 下午2:05
  8.  /
  9. @Entity
  10. public class Authority implements GrantedAuthority {
  11.     private static final long serialVersionUID = 1L;
  12.     @Id // 主键
  13.     @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增长策略
  14.     private Long id; // 用户的唯一标识
  15.     @Column(nullable = false// 映射为字段,值不能为空
  16.     private String name;
  17.     public Long getId() {
  18.         return id;
  19.     }
  20.     public void setId(Long id) {
  21.         this.id = id;
  22.     }
  23.     @Override
  24.     public String getAuthority() {
  25.         return name;
  26.     }
  27.     public void setName(String name) {
  28.         this.name = name;
  29.     }
  30. }

四、Dao 层

UserRepository.java

  1. package com.liuyanzhao.chuyun.repository;
  2. import com.liuyanzhao.chuyun.entity.User;
  3. import org.springframework.data.jpa.repository.JpaRepository;
  4. /
  5.   用户repository
  6.   @author 言曌
  7.   @date 2017/12/27 0027 20:50
  8.  /
  9. public interface UserRepository extends JpaRepository<User, Long> {
  10.     /
  11.       根据用户名查找用户
  12.       @param username
  13.       @return
  14.      /
  15.     User findByUsername(String username);
  16. }

五、Service 层

CustomUserService.java

  1. package com.liuyanzhao.chuyun.service;
  2. import com.liuyanzhao.chuyun.entity.User;
  3. import com.liuyanzhao.chuyun.repository.UserRepository;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.security.authentication.LockedException;
  6. import org.springframework.security.core.userdetails.UserDetailsService;
  7. import org.springframework.security.core.userdetails.UsernameNotFoundException;
  8. import org.springframework.stereotype.Service;
  9. /
  10.   @author 言曌
  11.   @date 2018/1/30 下午8:37
  12.  /
  13. @Service
  14. public class CustomUserService implements UserDetailsService{
  15.     @Autowired
  16.     private UserRepository userRepository;
  17.     @Override
  18.     public User loadUserByUsername(String username) throws UsernameNotFoundException {
  19.         User user = userRepository.findByUsername(username);
  20.         if (user == null) {
  21.             throw new UsernameNotFoundException(“用户名不存在”);
  22.         } else if(“locked”.equals(user.getStatus())) { //被锁定,无法登录
  23.             throw new LockedException(“用户被锁定”);
  24.         }
  25.         return user;
  26.     }
  27. }

六、Spring Security 配置

SecurityConfig.java

  1. package com.liuyanzhao.chuyun.config;
  2. import com.liuyanzhao.chuyun.service.CustomUserService;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.security.authentication.AuthenticationProvider;
  6. import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
  7. import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
  8. import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
  9. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  10. import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  11. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  12. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  13. import org.springframework.security.crypto.password.PasswordEncoder;
  14. /
  15.   安全配置类
  16.  
  17.   @author 言曌
  18.   @date 2018/1/23 上午11:37
  19.  /
  20. @EnableWebSecurity
  21. @EnableGlobalMethodSecurity(prePostEnabled = true// 启用方法安全设置
  22. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  23.     private static final String KEY = “liuyanzhao.com”;
  24.     @Autowired
  25.     private PasswordEncoder passwordEncoder;
  26.     @Bean
  27.     CustomUserService customUserService() {
  28.         return new CustomUserService();
  29.     }
  30.     @Bean
  31.     public PasswordEncoder passwordEncoder() {
  32.         return new BCryptPasswordEncoder();   // 使用 BCrypt 加密
  33.     }
  34.     @Bean
  35.     public AuthenticationProvider authenticationProvider() {
  36.         DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
  37.         authenticationProvider.setUserDetailsService(customUserService());
  38.         authenticationProvider.setPasswordEncoder(passwordEncoder); // 设置密码加密方式
  39.         return authenticationProvider;
  40.     }
  41.     /
  42.       自定义配置
  43.      /
  44.     @Override
  45.     protected void configure(HttpSecurity http) throws Exception {
  46.         http.authorizeRequests().antMatchers(“/”,“/css/”“/js/, “/fonts/”,“/users”).permitAll() // 都可以访问
  47.                 .antMatchers(“/h2-console/).permitAll() // 都可以访问
  48.                 .antMatchers(“/admin/”).hasRole(“ADMIN”// 需要相应的角色才能访问
  49.                 .antMatchers(“/console/).hasAnyRole(“ADMIN”,“USER”// 需要相应的角色才能访问
  50.                 .and()
  51.                 .formLogin()   //基于 Form 表单登录验证
  52.                 .loginPage(“/login”).failureUrl(“/login?error=true”// 自定义登录界面
  53.                 .and().rememberMe().key(KEY) // 启用 remember me
  54.                 .and().exceptionHandling().accessDeniedPage(“/403”);  // 处理异常,拒绝访问就重定向到 403 页面
  55.         http.csrf().ignoringAntMatchers(“/h2-console/”); // 禁用 H2 控制台的 CSRF 防护
  56.         http.headers().frameOptions().sameOrigin(); // 允许来自同一来源的H2 控制台的请求
  57.     }
  58.     /*
  59.       认证信息管理
  60.      
  61.       @param auth
  62.       @throws Exception
  63.      */
  64.     @Autowired
  65.     public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
  66.         //auth.userDetailsService(userDetailsService);
  67.         auth.userDetailsService(customUserService());
  68.         auth.authenticationProvider(authenticationProvider());
  69.     }
  70. }

七、HTML 页面

首页:http://localhost:8080

登录页面:http://localhost:8080/login

登录错误页面:http://localhost:8080/login?error=true

退出登录:http://localhost:8080/logout

index.html

  1. <!DOCTYPE html>
  2. <html xmlns=http://www.w3.org/1999/xhtml
  3.       xmlns:th=http://www.thymeleaf.org
  4.       xmlns:sec=http://www.thymeleaf.org/thymeleaf-extras-springsecurity4>
  5. <head>
  6.     <meta charset=“UTF-8”>
  7.     <meta name=“viewport”
  8.           content=“width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no”>
  9. </head>
  10. <body>
  11. <!–匿名–>
  12. <div sec:authorize=“isAnonymous()”>
  13.     未登录,点击 <a th:href=“@{/login}”>登录</a>
  14. </div>
  15. <!–登录–>
  16. <div sec:authorize=“isAuthenticated()”>
  17.     <p>已登录</p>
  18.     <p>登录名:<span sec:authentication=“name”></span></p>
  19.     <p>角色:<span sec:authentication=“principal.authorities”></span></p>
  20.     <p>Username:<span sec:authentication=“principal.username”></span></p>
  21.     <p>Password:<span sec:authentication=“principal.password”></span></p>
  22.     <p>Email :<span sec:authentication=“principal.email”></span></p>
  23.     <p>Name:<span sec:authentication=“principal.name”></span></p>
  24.     <p>Status:<span sec:authentication=“principal.status”></span></p>
  25.     <p>拥有的角色:
  26.     <span sec:authorize=“hasRole(‘ROLE_ADMIN’)”>管理员</span>
  27.     <span sec:authorize=“hasRole(‘ROLE_USER’)”>用户</span>
  28.     </p>
  29. </div>
  30. </body>
  31. </html>

login.html

  1. <!DOCTYPE html>
  2. <html xmlns=http://www.w3.org/1999/xhtml
  3.       xmlns:th=http://www.thymeleaf.org>
  4. <head>
  5.     <meta http-equiv=“Content-Type” content=“text/html; charset=utf-8”/>
  6.     <meta name=“viewport” content=“width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no”>
  7.     <title>登录页面</title>
  8. <body>
  9. <form th:action=“@{login}” method=“post” id=“loginForm”>
  10.     <span th:if= {param.error}"</span>&nbsp;<span class="attribute">th:text</span>=<span class="attribute-value">" {session.SPRING_SECURITY_LAST_EXCEPTION.message}”></span>
  11.     用户名:<input type=“text” name=“username” class=“username” id=“username” placeholder=“用户名” autocomplete=“off”/> <br>
  12.     密 码:<input type=“password” name=“password” class=“password” id=“password” placeholder=“密码”
  13.                 oncontextmenu=“return false”
  14.                 onpaste=“return false”/> <br>
  15.     <input type=“checkbox” name=“remember-me”/>记住我 <br>
  16.     <input id=“submit” type=“submit” value=“登录”/>
  17. </form>
  18. </body>
  19. </html>

八、运行效果

1、访问首页:http://localhost:8080

猜你喜欢

转载自blog.csdn.net/lwwl12/article/details/81285201