SpringBoot整合Spring Security (一,基于数据库的登录认证)
Posted little-monster-lhq
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot整合Spring Security (一,基于数据库的登录认证)相关的知识,希望对你有一定的参考价值。
SpringBoot整合Spring Security (一,基于数据库的登录认证)
一、基本环境准备
1、数据库表设计
登录认证一般涉及到三张表:用户表、角色表、用户角色中间表。
/*
Navicat MySQL Data Transfer
Source Server : localhost
Source Server Version : 50717
Source Host : localhost:3306
Source Database : security
Target Server Type : MYSQL
Target Server Version : 50717
File Encoding : 65001
Date: 2018-07-28 15:26:51
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) DEFAULT NULL,
`nameZh` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (\'1\', \'dba\', \'数据库管理员\');
INSERT INTO `role` VALUES (\'2\', \'admin\', \'系统管理员\');
INSERT INTO `role` VALUES (\'3\', \'user\', \'用户\');
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(32) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`enabled` tinyint(1) DEFAULT NULL,
`locked` tinyint(1) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (\'1\', \'root\', \'$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq\', \'1\', \'0\');
INSERT INTO `user` VALUES (\'2\', \'admin\', \'$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq\', \'1\', \'0\');
INSERT INTO `user` VALUES (\'3\', \'sang\', \'$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq\', \'1\', \'0\');
-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) DEFAULT NULL,
`rid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (\'1\', \'1\', \'1\');
INSERT INTO `user_role` VALUES (\'2\', \'1\', \'2\');
INSERT INTO `user_role` VALUES (\'3\', \'2\', \'2\');
INSERT INTO `user_role` VALUES (\'4\', \'3\', \'3\');
SET FOREIGN_KEY_CHECKS=1;
2.pom管理用到的依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<!--连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.14</version>
</dependency>
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--spring security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
3.配置文件
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/permission?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
mybatis:
mapper-locations: classpath:com/permission/mapper/*Mapper.xml
type-aliases-package: com.permission.domain
4.启动类
@SpringBootApplication
@MapperScan(value = "com.permission.mapper")
public class PermissionApplication
public static void main(String[] args)
SpringApplication.run(PermissionApplication.class,args);
5.实体类
- User
/**
* @Author:lvyuanjie
* @Date:2020/5/2 15:10
*/
public class User
private Integer id;
private String username;
private String password;
private Boolean enabled;
private Boolean locked;
private List<Role> roles;
//省略getter 、setter 方法
- Role
/**
* @Author:lvyuanjie
* @Date:2020/5/2 15:11
*/
public class Role
private Integer id;
private String name;
private String nameZh;
//省略getter 、setter 方法
6.Mapper 和 Mapper.xml
1.UserMapper
/**
* @Author:lvyuanjie
* @Date:2020/5/2 15:24
*/
public interface UserMapper
/**
* 通过用户名获取用户信息
*
* @param username 用户名
* @return User 用户信息
*/
User loadUserByUsername(String username);
/**
* 通过用户id获取用户角色集合
*
* @param userId 用户id
* @return List<Role> 角色集合
*/
List<Role> getUserRolesByUserId(Integer userId);
2.UserMapper.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.permission.mapper.UserMapper">
<select id="loadUserByUsername" resultType="com.permission.domain.User">
select * from user where username=#username
</select>
<select id="getUserRolesByUserId" resultType="com.permission.domain.Role">
select * from role r,user_role ur where r.id=ur.rid and ur.uid=#userId
</select>
</mapper>
二、使用Spring Security的三步(实现三个Spring Security的接口)
1.实现 UserDetails 接口
这个接口是用来将用户的信息给spring security框架进行验证用的,实现方式就是用户表的实体类实现这个接口就可以
/**
* @Author:lvyuanjie
* @Date:2020/5/2 15:10
*/
public class User implements UserDetails
private Integer id;
private String username;
private String password;
private Boolean enabled;
private Boolean locked;
private List<Role> roles;
public Integer getId()
return id;
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 Boolean getEnabled()
return enabled;
public void setEnabled(Boolean enabled)
this.enabled = enabled;
public Boolean getLocked()
return locked;
public void setLocked(Boolean locked)
this.locked = locked;
public List<Role> getRoles()
return roles;
public void setRoles(List<Role> roles)
this.roles = roles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities()
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (Role role : roles)
authorities.add(new SimpleGrantedAuthority(role.getName()));
return authorities;
@Override
public String getPassword()
return password;
@Override
public String getUsername()
return username;
@Override
public boolean isAccountNonExpired()
return true;
@Override
public boolean isAccountNonLocked()
return !locked;
@Override
public boolean isCredentialsNonExpired()
return true;
@Override
public boolean isEnabled()
return enabled;
UserDetails 接口的7个方法
方法名 | 解释 |
---|---|
getAuthorities() | 获取当前用户的所有角色信息 |
getPassword() | 获取当前用户的密码 |
getUsername() | 获取当前用户的用户名 |
isAccountNonExpired() | 当前账户是否未过期 |
isAccountNonLocked() | 当前账户是否未锁定 |
isCredentialsNonExpired() | 当前账户密码是否未过期 |
isEnabled() | 当前账户是否可用 |
2.实现 UserDetailsService 接口
这个接口只有一个方法:loadUserByUsername(String s) ,s是用户登录时输入的账户名,这个方法就是通过用户输入的账户名判断该账户是否存在,存在才进行用户角色信息的查找,然后才有DaoAuthenticationProvider校验密码
/**
* @Author:lvyuanjie
* @Date:2020/5/2 15:22
*/
@Service
public class UserService implements UserDetailsService
@Autowired
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException
User user = userMapper.loadUserByUsername(s);
if (user == null)
throw new UsernameNotFoundException("该用户不存在!");
user.setRoles(userMapper.getUserRolesByUserId(user.getId()));
return user;
3.继承 WebSecurityConfigurerAdapter
1.将刚创建的UserService 配置到AuthenticationManagerBuilder 中
- AuthenticationSuccessHandler() :自定义登录成功或失败的返回信息(这里采用的匿名内部类配的,也可以单独写一个类来配置)
3.successHandler():将自定义的登录成功信息配置进去
- failureHandler():将自定义登录失败的信息配置进去
/**
* @Author:lvyuanjie
* @Date:2020/5/2 11:15
*/
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
@Autowired
UserService userService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
auth.userDetailsService(userService)
.passwordEncoder(new BCryptPasswordEncoder());
@Override
protected void configure(HttpSecurity http) throws Exception
http.authorizeRequests()//
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasRole("user")
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/login").permitAll()
.successHandler(new AuthenticationSuccessHandler()
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication auth) throws IOException, ServletException
Object principal = auth.getPrincipal();
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
response.setStatus(200);
Map<String, Object> map = new HashMap<>(16);
map.put("status",200);
map.put("msg",principal);
ObjectMapper om = new ObjectMapper();
out.write(om.writeValueAsString(map));
out.flush();
out.close();
)
.failureHandler(new AuthenticationFailureHandler()
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
response.setStatus(401);
Map<String, Object> map = new HashMap<>(16);
map.put("status", 401);
if (e instanceof LockedException)
map.put("msg", "账户被锁定,登录失败!");
else if (e instanceof BadCredentialsException)
map.put("msg", "账户名或密码输入错误,登录失败!");
else if (e instanceof DisabledException)
map.put("msg", "账户被禁用,登录失败!");
else if (e instanceof AccountExpiredException)
map.put("msg", "账户已过期,登录失败!");
else if (e instanceof CredentialsExpiredException)
map.put("msg", "密码已过期,登录失败!");
else
map.put("msg", "登录失败!");
ObjectMapper om = new ObjectMapper();
out.write(om.writeValueAsString(map));
out.flush();
out.close();
)
.and()
.csrf().disable();
4.工程结构
三、测试
账户名:admin 密码:123
使用错误密码登录
以上是关于SpringBoot整合Spring Security (一,基于数据库的登录认证)的主要内容,如果未能解决你的问题,请参考以下文章
企业分布式微服务云SpringCloud SpringBoot mybatis (十三)Spring Boot整合MyBatis