7.1 新建项目
7.2 pom.xml引入依赖
<!--Thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--SpringBoot security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--页面上获取用户信息-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
<!--引入数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.7</version>
</dependency>
<!--引入mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!--引入mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
7.3 application.properties添加配置
# 配置Thymeleaf
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML
# datasource:类型、驱动名、用户名、密码
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/security?characterEncoding=UTF-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
# mybatis配置mapper文件的位置和别名设置
# 注意mapper目录(包)新建时必须使用"/",而不是.
mybatis.mapper-locations=classpath:com/study/mapper/*.xml
mybatis.type-aliases-package=com.study.entity
# log:为了显示mybatis运行SQL语句
logging.level.com.study=debug
7.4 创建实体类entity
User
package com.study.entity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.*;
/**
* @ClassName User
* @Description TODO
* @Date 2022/8/7 15:16
* @Version 1.0
*/
public class User implements UserDetails {
private Integer id;
private String username;
private String password;
private Boolean enabled;
private Boolean accountNonExpired;
private Boolean accountNonLocked;
private Boolean credentialsNonExpired;
private List<Role> roles = new ArrayList<>();//关系属性,用来存储当前用户所有角色信息
//返回权限信息
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<SimpleGrantedAuthority> authorities = new HashSet<>();
roles.forEach(role -> {
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(role.getName());
authorities.add(simpleGrantedAuthority);
});
return authorities;
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public boolean isAccountNonExpired() {
return accountNonLocked;
}
@Override
public boolean isAccountNonLocked() {
return accountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return credentialsNonExpired;
}
@Override
public boolean isEnabled() {
return enabled;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public Boolean getAccountNonExpired() {
return accountNonExpired;
}
public void setAccountNonExpired(Boolean accountNonExpired) {
this.accountNonExpired = accountNonExpired;
}
public Boolean getAccountNonLocked() {
return accountNonLocked;
}
public void setAccountNonLocked(Boolean accountNonLocked) {
this.accountNonLocked = accountNonLocked;
}
public Boolean getCredentialsNonExpired() {
return credentialsNonExpired;
}
public void setCredentialsNonExpired(Boolean credentialsNonExpired) {
this.credentialsNonExpired = credentialsNonExpired;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
}
Role
package com.study.entity;
/**
* @ClassName Role
* @Description TODO
* @Date 2022/8/7 15:17
* @Version 1.0
*/
public class Role {
private Integer id;
private String name;
private String nameZh;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNameZh() {
return nameZh;
}
public void setNameZh(String nameZh) {
this.nameZh = nameZh;
}
}
7.5 创建UserDao
package com.study.dao;
import com.study.entity.Role;
import com.study.entity.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* @ClassName UserDao
* @Description TODO
* @Date 2022/8/7 15:19
* @Version 1.0
*/
@Mapper
public interface UserDao {
/**
* 根据用户名查找用户
*
* @param username
* @return
*/
User loadUserByUsername(String username);
/**
* 根据用户id查询一个角色,注意一个用户可能不止一种角色
*
* @param uid
* @return
*/
List<Role> getRolesByUid(Integer uid);
}
7.6 创建UserDAOMapper.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.study.dao.UserDao">
<!--loadUserByUsername-->
<select id="loadUserByUsername" resultType="User">
select id,
username,
password,
enabled,
accountNonExpired,
accountNonLocked,
credentialsNonExpired
from user
where username = #{username}
</select>
<!--getRolesByUid
需要将角色表和用户-角色表进行关联查询,查询条件为role.id=user_role.uid
其中,uid是外界传入的参数
-->
<select id="getRolesByUid" resultType="Role">
select r.id,
r.name,
r.name_zh nameZh
from role r,
user_role ur
where r.id = ur.rid
and ur.uid = #{uid}
</select>
</mapper>
7.7 创建MyUserDetailsService
package com.study.service;
import com.study.dao.UserDao;
import com.study.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;
import org.springframework.util.ObjectUtils;
/**
* @ClassName MyUserDetailsService
* @Description TODO
* @Date 2022/8/7 15:15
* @Version 1.0
*/
@Service
public class MyUserDetailsService implements UserDetailsService {
private final UserDao userDao;
@Autowired
public MyUserDetailsService(UserDao userDao) {
this.userDao = userDao;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.loadUserByUsername(username);
if (ObjectUtils.isEmpty(user))
throw new UsernameNotFoundException("用户不存在");
user.setRoles(userDao.getRolesByUid(user.getId()));
return user;
}
}
7.8 编写html
login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<h1>用户登录</h1>
<form method="post" th:action="@{/doLogin}">
用户名:<input name="uname" type="text"><br>
密码:<input name="pwd" type="password"><br>
<input type="submit" value="登录">
</form>
<h2>
<!-- <div th:text="${SPRING_SECURITY_LAST_EXCEPTION}"></div>--><!--request作用域:forward跳转-->
<div th:text="${session.SPRING_SECURITY_LAST_EXCEPTION}"></div><!--session作用域:redirect跳转-->
</h2>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en" xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>系统主页</title>
</head>
<body>
<h1>欢迎<span sec:authentication="principal.username"></span>,进入我的系统!</h1>
<h3>用户详细信息如下:</h3>
<ul>
<li sec:authentication="principal.username"></li>
<li sec:authentication="principal.authorities"></li>
<li sec:authentication="principal.accountNonExpired"></li>
<li sec:authentication="principal.accountNonLocked"></li>
<li sec:authentication="principal.credentialsNonExpired"></li>
</ul>
<br>
<a th:href="@{/logout}">退出登录</a>
</body>
</html>
7.9 编写MvcConfigurer
package com.study.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @ClassName MvcConfigurer
* @Description 对SpringMVC进行自定义配置
* @Date 2022/8/7 14:20
* @Version 1.0
*/
@Configuration
public class MvcConfigurer implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//addViewController: forward跳转
registry.addViewController("/login.html").setViewName("login");
registry.addViewController("/index.html").setViewName("index");
}
}
7.10 编写SecurityConfigurer
package com.study.config;
import com.study.service.MyUserDetailsService;
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.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* @ClassName SecurityConfiger
* @Description 自定义security相关配置
* @Date 2022/8/7 14:35
* @Version 1.0
*/
@Configuration
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {
// @Bean
// public UserDetailsService userDetailsService() {
// InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
// userDetailsManager.createUser(User.withUsername("root").password("{noop}123456").roles("admin").build());
// return userDetailsManager;
// }
//自定义数据源注入
@Autowired
private MyUserDetailsService myUserDetailsService;
//自定义全局配置AuthenticationManager
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.mvcMatchers("/login.html").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html") //指定自定义登录页面
.loginProcessingUrl("/doLogin")
.usernameParameter("uname")
.passwordParameter("pwd")
.defaultSuccessUrl("/index.html", true)
.failureUrl("/login.html")
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login.html")
.and()
.csrf().disable();
}
}
7.11 启动服务测试
(1)登录数据库中含有的用户时,登录成功后显示认证信息:
(2)登录数据库中不含有的用户时,认证后显示错误信息: