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管理用到的依赖

    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.7.RELEASE
         
    

    
        1.8
    

    
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            2.1.2
        
        
        
            com.alibaba
            druid-spring-boot-starter
            1.1.14
        
        
        
            mysql
            mysql-connector-java
            runtime
        
        
        
            org.springframework.boot
            spring-boot-starter-security
        

    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

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.实体类

  1. 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 roles;
    
//省略getter 、setter 方法
  1. 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 角色集合
     */
    List getUserRolesByUserId(Integer userId);
}

2.UserMapper.xml




    

    

二、使用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 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 getRoles() {
        return roles;
    }

    public void setRoles(List roles) {
        this.roles = roles;
    }

    @Override
    public Collection getAuthorities() {
        List 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 中
2. AuthenticationSuccessHandler() :自定义登录成功或失败的返回信息(这里采用的匿名内部类配的,也可以单独写一个类来配置)
3.successHandler():将自定义的登录成功信息配置进去
4. 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 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 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.工程结构

SpringBoot整合Spring Security (一,基于数据库的登录认证)_第1张图片

三、测试

账户名:admin 密码:123

SpringBoot整合Spring Security (一,基于数据库的登录认证)_第2张图片

使用错误密码登录

SpringBoot整合Spring Security (一,基于数据库的登录认证)_第3张图片

你可能感兴趣的:(SpringBoot,Spring,Security)