springboot+springsecurity+thymeleaf+mybatis整合
首先我们要考虑的是如何将springsecurity自带的用户类UserDetails 与我们项目的用户类绑定起来
方式一:重写UserDetailsService的loadUserByUsername,通过在数据库查寻到的数据为springsecurity自带的用户类UserDetails赋值,参考SpringSecurity使用 中的“通过数据库授权”
方式二:就是让系统的用户类继承springsecurity自带的用户类UserDetails,也就是本文使用的方式
数据库建模
用户表sys_user
角色表sys_role
用户和角色的联系表sys_user_role
创建项目
添加pom.xml依赖
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springbootdome2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springbootdome2</name>
<description>Demo project for Spring Boot</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--mysql数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<!-- mybatis逆向工程jar包 -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.4</version>
</dependency>
<!--security认证授权-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--thymeleaf引擎模板-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--对Thymeleaf添加Spring Security标签支持-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<overwrite>true</overwrite>
<!--逆向工程generatorConfig.xml文件位置-->
<configurationFile>src/main/resources/generator/generatorConfig.xml</configurationFile>
</configuration>
</plugin>
</plugins>
</build>
</project>
配置项目
server.port=8081
#数据库连接配置
spring.datasource.url=jdbc:mysql://ip:3306/sweetinn?characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=用户名
spring.datasource.password=密码
#mybatis整合
mybatis.type-aliases-package=com.example.demo.pojo
mybatis.mapper-locations= classpath:mapper/*.xml
logging.level.com.example.demo.dao=debug
#关闭thymeleaf缓存
spring.thymeleaf.cache=false
创建实体类
创建用户类SysUser并继承springsecurity中的UserDetails重写方法
package com.example.demo.pojo;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
public class SysUser implements UserDetails {
public SysUser() {
}
public SysUser(String username, String password, Boolean isenable, List<? extends GrantedAuthority> authorities, String realName, String createTime, String loginTime) {
this.username = username;
this.password = password;
this.isenable = isenable;
this.authorities = authorities;
this.realName = realName;
this.createTime = createTime;
this.loginTime = loginTime;
}
private Integer id;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 是否可用
*/
private Boolean isenable;
/**
*用户所拥有的权限
*/
private List<? extends GrantedAuthority> authorities;
/**
* 用户的账号是否过期,过期的账号无法通过授权验证. true 账号未过期
*/
private Boolean accountNonExpired = true;
/**
* 用户的账户是否被锁定,被锁定的账户无法通过授权验证. true 账号未锁定
*/
private Boolean islock = true;
/**
* 用户的凭据(pasword) 是否过期,过期的凭据不能通过验证. true 没有过期,false 已过期
*/
private Boolean iscredentials = true;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return accountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return islock;
}
@Override
public boolean isCredentialsNonExpired() {
return iscredentials;
}
@Override
public boolean isEnabled() {
return isenable;
}
private String realName;
private String createTime;
private String loginTime;
public void setId(Integer id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setIsenable(Boolean isenable) {
this.isenable = isenable;
}
public void setAuthorities(List<? extends GrantedAuthority> authorities) {
this.authorities = authorities;
}
public void setAccountNonExpired(Boolean accountNonExpired) {
this.accountNonExpired = accountNonExpired;
}
public void setIslock(Boolean islock) {
this.islock = islock;
}
public void setIscredentials(Boolean iscredentials) {
this.iscredentials = iscredentials;
}
public void setRealName(String realName) {
this.realName = realName;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
public void setLoginTime(String loginTime) {
this.loginTime = loginTime;
}
public Integer getId() {
return id;
}
public Boolean getIsenable() {
return isenable;
}
public String getRealName() {
return realName;
}
public String getCreateTime() {
return createTime;
}
public String getLoginTime() {
return loginTime;
}
}
创建角色类SysRole
package com.example.demo.pojo;
public class SysRole {
private Integer id;
private String roleName;
private String roleMemo;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleMemo() {
return roleMemo;
}
public void setRoleMemo(String roleMemo) {
this.roleMemo = roleMemo;
}
}
创建dao(mapper)层
SysUserMapper
@Repository
public interface SysUserMapper {
int insertSysUser(SysUser record);
SysUser findSysUser(String username);
}
SysUserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.demo.dao.SysUserMapper" >
<resultMap id="SysUserResultMap" type="com.example.demo.pojo.SysUser" >
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
-->
<id column="id" property="id"/>
<result column="user_name" property="username"/>
<result column="password" property="password"/>
<result column="real_name" property="realName"/>
<result column="isenable" property="isenable"/>
<result column="islock" property="islock"/>
<result column="iscredentials" property="iscredentials"/>
<result column="create_time" property="createTime"/>
<result column="login_time" property="loginTime"/>
</resultMap>
<insert id="insertSysUser" parameterType="com.example.demo.pojo.SysUser">
INSERT INTO sys_user (user_name,password,real_name,isenable,islock,iscredentials,create_time,login_time)
VALUES (#{username},#{password},#{realName},#{isenable},#{islock},#{iscredentials},#{createTime},#{loginTime})
</insert>
<select id="findSysUser" resultMap="SysUserResultMap">
SELECT id,user_name,password,real_name,isenable,islock,iscredentials,create_time,login_time
FROM sys_user
where user_name=#{username}
</select>
</mapper>
SysRoleMapper
@Repository
public interface SysRoleMapper {
List<SysRole> findRoleByUserId(Integer userId);
}
SysRoleMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.demo.dao.SysRoleMapper" >
<resultMap id="SysRoleResultMap" type="com.example.demo.pojo.SysRole" >
<id column="id" property="id" />
<result column="role_name" property="roleName"/>
<result column="role_memo" property="roleMemo"/>
</resultMap>
<select id="findRoleByUserId" resultMap="SysRoleResultMap">
SELECT r.id,r.role_name,r.role_memo
FROM sys_user_role ur,sys_role r
WHERE ur.role_id=r.id and ur.user_id=#{userId}
</select>
</mapper>
自定义UserDetailsService的子类
@Service("jdbcUserDetailService")
public class JdbcUserDetailService implements UserDetailsService {
@Autowired
SysUserMapper sysUserMapper;
@Autowired
SysRoleMapper sysRoleMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser sysUser = sysUserMapper.findSysUser(username);
if(sysUser!=null){
List<SysRole> roleByUserId = sysRoleMapper.findRoleByUserId(sysUser.getId());
List<GrantedAuthority> list=new ArrayList<>();
for (SysRole sysRole:roleByUserId) {
GrantedAuthority grantedAuthority=new SimpleGrantedAuthority("ROLE_"+sysRole.getRoleName());
list.add(grantedAuthority);
}
sysUser.setAuthorities(list);
}
return sysUser;
}
}
配置springsecurity
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier(value = "jdbcUserDetailService")
UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
//认证
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/","/index","/toLogin","/login").permitAll()
.antMatchers("/level1/**").hasRole("level1")
.antMatchers("/level2/**").hasRole("level2")
.antMatchers("/level3/**").hasRole("level3");
http.formLogin().loginPage("/toLogin") //指定自定义的登录页面
.usernameParameter("name") //指定自定义的登录页面用户名
.passwordParameter("passwd") //指定自定义的登录页面密码
.loginProcessingUrl("/toLogin");//表单提交的路径
http.csrf().disable();
}
}
启动类
@MapperScan("com.example.demo.dao")//启动项目时扫描dao层接口
@SpringBootApplication
public class Springbootdome2Application {
@Autowired
SysUserMapper sysUserMapper;
public static void main(String[] args) {
SpringApplication.run(Springbootdome2Application.class, args);
}
//启动项目时向数据库用户表添加用户
/* @PostConstruct
public void jdbcInit(){
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
List<GrantedAuthority> list=new ArrayList<>();
SysUser sysUser = new SysUser("root",encoder.encode("123456"),true,list,"root","2021-02-25","2021-02-25");
sysUserMapper.insertSysUser(sysUser);
}*/
}
前端页面
mylogin.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<div style="text-align: center">
登录
<form th:action="@{/toLogin}" method="post">
登录名:<input name="name" /> <br/>
密码:<input type="password" name="passwd"/><br/>
<input type="checkbox" name="jzw"> 记住我<br/>
<input type="submit" />
</form>
</div>
</body>
</html>
home.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<div sec:authorize="!isAuthenticated()">
<a th:href="@{/toLogin}">登录</a>
</div>
<div sec:authorize="isAuthenticated()">
用户名:<span sec:authentication="principal.username"></span>
角色:<span sec:authentication="principal.authorities"></span>
<a th:href="@{/logout}">注销</a>
</div>
</div>
<div sec:authorize="hasRole('level1')">
<a th:href="@{/level1/page/1}">level1-page1</a>
<a th:href="@{/level1/page/2}">level1-page2</a>
<a th:href="@{/level1/page/3}">level1-page3</a>
</div>
<div sec:authorize="hasRole('level2')">
<a href="/level2/page/1">level2-page1</a>
<a href="/level2/page/2">level2-page2</a>
<a href="/level2/page/3">level2-page3</a>
</div>
<div sec:authorize="hasRole('level3')">
<a href="/level3/page/1">level3-page1</a>
<a href="/level3/page/2">level3-page2</a>
<a href="/level3/page/3">level3-page3</a>
</div>
</body>
</html>
补充:1.数据库的用户密码加密方式必须和springsecurity的认证授权加密方式一致
2.数据库数据