1.创建Spring Boot+Mybatis的maven项目
- 在IDEA中点击File——New——Project新建项目,选择好jdk后点击next:
- 在此页面可以修改项目的信息,默认信息如下:
- 点击next到勾选依赖页面,勾选
web
、mysql
、jdbc
、mybatis
如下:
- 点击next后填写项目名称,点击Finish创建完成,等待maven下载依赖包完成,项目结构如下:
-
可以看到maven的
pom.xml
文件中已经添加了刚才勾选的web
、mysql
、jdbc
、mybatis
依赖,内容如下:<?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 http://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.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
-
创建数据库,例子中数据库名称为
demo
:
- 在
resources
下的application.properties
中配置数据库连接参数:#数据库连接 spring.datasource.url=jdbc:mysql://localhost:3306/demo?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=你的密码 spring.datasource.driver-class-name=com.mysql.jdbc.Driver
- 增加
IndexController
和index.html
页面:
@Controller
public class IndexController {
@GetMapping("/")
public String index() {
return "index";
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
访问成功
</body>
</html>
-
注意:如果你的
controller
和启动类Application
不在同一包下,则需要@ComponentScan
注解来指定需要加载的bean路径。 -
因为页面放到了
templates
下,所以需要在pom.xml
文件中加入thymeleaf
依赖包:<!--thymeleaf依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
-
到此项目就创建完成。启动项目,访问
localhost:8080
,成功页面如下:
2.Spring Security
配置
2.1 使用Spring Security
原生的页面登录
-
在
pom.xml
文件中加入Spring Security依赖包:<!--security依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
-
此时
Spring Security
已经生效。如果重新启动项目,访问localhost:8080
,会重定向到localhost:8080/login
页面,这是Spring Security
自带的登录页面,如下:
- 默认的用户名为
user
,密码会在启动项目时输出到控制台:
- 输入用户名和控制台打印的密码,登录成功即可跳转到首页。
2.2 通过配置,使用自定义的页面登录
- 增加
LoginController
和login.html
页面:
@Controller
public class LoginController {
@GetMapping("/login")
public String login(){
return "login";
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>登录</title>
</head>
<body>
<div id="login-box">
<form th:action="@{/login}" method="post">
帐号:<input type="text" name="username">
<br/>
密码:<input type="password" name="password">
<br/>
<button type="submit">登录</button>
<span th:if="${param.error}">登录失败</span>
<span th:if="${param.logout}">退出登录成功</span>
</form>
</div>
</body>
</html>
-
修改
index.html
页面,加入退出登录按钮:<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> 访问成功 <a th:href="@{/logout}">点击退出登录</a> </body> </html>
-
添加
CustomUserService
并实现UserDetailsService
,用于根据表单提交的用户名加载对应的用户信息(后续改为从数据库加载用户):
public class CustomUserService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) {
//添加用户的角色,用于权限验证
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
//创建用户:用户名user,密码123,角色ADMIN
return new User("user", this.passwordEncoder.encode("123"), authorities);
}
}
- 增加
WebSecurityConfig
配置类,继承WebSecurityConfigurerAdapter
来配置访问规则:
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//用于密码加密
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
//将CustomUserService注册为bean
@Bean
public CustomUserService customUserService() {
return new CustomUserService();
}
//使用自定义的CustomUserService加载用户
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.customUserService());
super.configure(auth);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login")//设置登录页面
.loginProcessingUrl("/login")//自定义的登录接口
.failureUrl("/login?error")//登录失败的页面
.and()
.authorizeRequests()
.antMatchers("/login").permitAll()//设置不需要登陆就能访问登录页面
.anyRequest()//登录后可以访问所有页面
.authenticated()
.and()
.csrf().disable();//关闭csrf防护
}
}
- 到此使用自定义页面登录就配置成功了,重启项目,此时
localhost:8080/login
显示的是自定义的登录页面:
- 输入用户名user,密码123即可登录成功:
2.3 配置Mybatis
动态查询数据库的用户和角色
-
数据库中创建表
sys_user
、sys_role
、sys_user_role
,用来记录用户、角色、用户和角色的关系:CREATE TABLE `sys_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `user_name` varchar(16) DEFAULT NULL, `password` varchar(128) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; CREATE TABLE `sys_role` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(16) DEFAULT NULL COMMENT '角色名称', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; CREATE TABLE `sys_user_role` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `user_id` bigint(20) DEFAULT NULL, `role_id` bigint(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
-
手动在表中添加数据:
-
sys_user
表,注意:密码是用new BCryptPasswordEncoder().encode("123")
加密后的数据。
sys_role
表。
sys_user_role
表。
-
在
resources
下的application.properties
中开启驼峰映射:#数据库连接 spring.datasource.url=jdbc:mysql://localhost:3306/demo?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=你的密码 spring.datasource.driver-class-name=com.mysql.jdbc.Driver #驼峰映射 mybatis.configuration.map-underscore-to-camel-case=true
-
驼峰映射就是将数据库中的
xxx_yyy
字段映射为实体类中的xxxYyy
属性,比如:字段user_name
映射的是实体类中的userName
属性。实现Mybatis的映射有四种方式,具体可以参考另一篇文章Mybatis数据库字段和实体属性的映射 -
创建Mybatis的实体类、mapper和service,用来查询数据库:
-
创建实体:
public class SysUser { private Long id; private String userName; private String password; private List<SysRole> sysRoleList; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public List<SysRole> getSysRoleList() { return sysRoleList; } public void setSysRoleList(List<SysRole> sysRoleList) { this.sysRoleList = sysRoleList; } }
public class SysRole { private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
public class SysUserRole { private Long id; private Long userId; private Long roleId; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public Long getRoleId() { return roleId; } public void setRoleId(Long roleId) { this.roleId = roleId; } }
- 创建
Mapper
用来执行查询语句: -
因为为了省事使用了驼峰映射,所以不需要使用
xml
文件来配置字段和实体的映射关系,直接使用注解编写sql即可。@Mapper @Repository public interface SysUserMapper { @Select("SELECT * FROM sys_user") List<SysUser> findAll(); @Select("SELECT * FROM sys_user WHERE user_name = #{userName}") SysUser findByUserName(@Param("userName") String userName); }
@Mapper @Repository public interface SysRoleMapper { @Select("SELECT * FROM sys_role WHERE id = #{id}") SysRole findById(@Param("id") Long id); }
@Mapper @Repository public interface SysUserRoleMapper { @Select("SELECT * FROM sys_user_role WHERE user_id = #{userId}") List<SysUserRole> findByUserId(@Param("userId") Long userId); }
-
创建
service
用来编写业务代码:@Service public class SysUserService { @Autowired private SysUserMapper sysUserMapper; public List<SysUser> findAll() { return sysUserMapper.findAll(); } public SysUser findByUserName(String userName) { return sysUserMapper.findByUserName(userName); } }
@Service public class SysRoleService { @Autowired private SysRoleMapper sysRoleMapper; public SysRole findById(Long id) { return sysRoleMapper.findById(id); } }
@Service public class SysUserRoleService { @Autowired private SysUserRoleMapper sysUserRoleMapper; public List<SysUserRole> findByUserId(Long userId) { return sysUserRoleMapper.findByUserId(userId); } }
-
修改
WebSecurityConfig
配置类,设置首页需要ADMIN
角色才能访问:@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { //用于密码加密 @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } //将CustomUserService注册为bean @Bean public CustomUserService customUserService() { return new CustomUserService(); } //使用自定义的CustomUserService加载用户 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(this.customUserService()); super.configure(auth); } @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() .loginPage("/login")//设置登录页面 .loginProcessingUrl("/login")//自定义的登录接口 .failureUrl("/login?error")//登录失败的页面 .and() .authorizeRequests() .antMatchers("/login").permitAll()//设置不需要登陆就能访问登录页面 .antMatchers("/").hasAnyRole("ADMIN")//设置首页需要ADMIN角色才能访问 .anyRequest()//登录后可以访问所有页面 .authenticated() .and() .csrf().disable();//关闭csrf防护 } }
- 修改
CustomUserService
配置类,改为从数据库获取用户和角色: -
需要注意,数据库保存的密码必须是用
new BCryptPasswordEncoder().encode("密码")
加密后的数据,否则不能登录。public class CustomUserService implements UserDetailsService { @Autowired private SysUserMapper userMapper; @Autowired private SysUserRoleMapper sysUserRoleMapper; @Autowired private SysRoleMapper sysRoleMapper; @Override public UserDetails loadUserByUsername(String username) { //通过用户名查询用户 SysUser user = userMapper.findByUserName(username); if (user == null) { throw new UsernameNotFoundException("用户名不存在"); } //查询用户的角色信息 List<SimpleGrantedAuthority> authorities = new ArrayList<>(); List<SysUserRole> sysUserRoleList = sysUserRoleMapper.findByUserId(user.getId()); for (SysUserRole sysUserRole : sysUserRoleList) { SysRole sysRole = sysRoleMapper.findById(sysUserRole.getRoleId()); if (sysRole != null) { authorities.add(new SimpleGrantedAuthority(sysRole.getName())); } } //返回查询到的用户,验证能否登录 return new User(user.getUserName(), user.getPassword(), authorities); } }
- 重新启动项目,访问
localhost:8080
,此时会跳转到自定义登录页面,输入刚才添加到数据库中的用户名和密码(密码是加密前的明文密码,例子中的用户名:user
密码:123
),点击登录,页面报错如下:
- 报错信息为403表示服务器拒绝访问,说明我们配置的角色起作用了。拒绝访问是因为数据库中的角色是
USER
,而首页需要ADMIN
角色才允许访问。 - 我们将数据库表
sys_role
中的角色ROLE_USER
改为ROLE_ADMIN
,重新登录:
- 登录成功了: