Spring Security 从数据库加载用户名和密码

Spring Security 从数据库加载用户名和密码

项目结构

数据库设计

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for ROLE
-- ----------------------------
DROP TABLE IF EXISTS `ROLE`;
CREATE TABLE `ROLE` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of ROLE
-- ----------------------------
BEGIN;
INSERT INTO `ROLE` VALUES (1, 'ROLE_ADMIN');
INSERT INTO `ROLE` VALUES (2, 'ROLE_USER');
COMMIT;

-- ----------------------------
-- Table structure for ROLE_USER
-- ----------------------------
DROP TABLE IF EXISTS `ROLE_USER`;
CREATE TABLE `ROLE_USER` (
  `user_id` int DEFAULT NULL,
  `role_id` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of ROLE_USER
-- ----------------------------
BEGIN;
INSERT INTO `ROLE_USER` VALUES (1, 1);
INSERT INTO `ROLE_USER` VALUES (2, 2);
COMMIT;

-- ----------------------------
-- Table structure for USER
-- ----------------------------
DROP TABLE IF EXISTS `USER`;
CREATE TABLE `USER` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(10) DEFAULT NULL,
  `password` varchar(150) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of USER
-- ----------------------------
BEGIN;
INSERT INTO `USER` VALUES (1, 'root', '$2a$10$YZeZCBa8GNB3STzcOcLnh.gUTt5zdc8lvSOgubQaONbuJNrox3K16');
INSERT INTO `USER` VALUES (2, 'ming', '$2a$10$qv2hxWT3PsYBA0RwTfttJ.JhhvwBm6dSqZmAEI2gJSGyCo.iXOMG6');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

Mapper

MyUser

public class MyUser {
    private Integer id;
    private String name;
    private String password;

    public MyUser(){}
    //省略get set方法
}

Role

public class Role {
    private Integer id;
    private String name;

    public Role(){}
    //省略get set方法
}

LoginMapper

@Mapper
@Component  //把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class="">
public interface LoginMapper {
    @Select("select * from user where name = #{name}")
    MyUser loadUserByUsername(String name);

    @Select("SELECT role.`name` FROM role WHERE role.id in (SELECT role_id FROM " +
            " role_user as r_s JOIN `user` as u  ON r_s.user_id = u.id and u.id = #{id})")
    List<Role> findRoleByUserId(int id);
}

自定义 UserDetailsService

UserDetailsService 的主要作用是获取数据库里面的信息,然后封装成user对象。

实现自己的 UserDetailsService ,从数据库中获取用户名和密码信息。

UserService

public interface UserService extends UserDetailsService {
}

UserServiceImpl

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private LoginMapper loginMapper;

    @Override
    public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
        // 根据用户名查询数据库,查到对应的用户
        MyUser myUser = loginMapper.loadUserByUsername(name);

        // ... 做一些异常处理,没有找到用户之类的
        if(myUser == null){
            throw new UsernameNotFoundException("用户不存在") ;
        }

        // 根据用户ID,查询用户的角色
        List<Role> roles = loginMapper.findRoleByUserId(myUser.getId());
        // 添加角色
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (int i = 0; i < roles.size(); i++) {
            authorities.add(new SimpleGrantedAuthority(roles.get(i).getName()));
        }
        // 构建 Security 的 User 对象
        User user = new User(myUser.getName(), myUser.getPassword(), authorities);

        return user;
    }
}

自定义登陆校验器 AuthenticationProvider

实现自己的 AuthenticationProvider 类,完成校验逻辑。

@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
    @Autowired
    private UserService userService;

    /**
     * 进行身份认证
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        // 获取用户输入的用户名和密码
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();

        // 获取封装用户信息的对象
        UserDetails userDetails = userService.loadUserByUsername(username);
        // 进行密码的比对
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        boolean flag = bCryptPasswordEncoder.matches(password, userDetails.getPassword());

        // 校验通过
        if (flag){
            // 将权限信息也封装进去
            return new UsernamePasswordAuthenticationToken(userDetails,password,userDetails.getAuthorities());
        }

        // 验证失败返回 null
        return null;
    }

    /**
     * 这个方法 确保返回 true 即可
     */
    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }

配置 security

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//    WebSecurityConfigurerAdapter类:通过重载该类的三个configure()方法来制定Web安全的细节。
//            1、configure(WebSecurity):通过重载该方法,可配置Spring Security的Filter链。
//            2、configure(HttpSecurity):通过重载该方法,可配置如何通过拦截器保护请求。
//            3、configure(AuthenticationManagerBuilder):通过重载该方法,可配置user-detail(用户详细信息)服务。
    @Autowired
    private MyAuthenticationProvider myAuthenticationProvider;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()    //开启登录配置
                .antMatchers("/", "/home").permitAll()  //访问"/"和"/home"路径的请求都允许
                .anyRequest().authenticated()   //表示剩余的其他接口,登录之后访问
                .and()
                .formLogin()    //表单登陆
                //定义登录页面,未登录时,访问一个需要登录之后才能访问的接口,会自动跳转到该页面
                .loginPage("/login")
                .permitAll()
                .and()
                .logout()
                .permitAll();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth.authenticationProvider(myAuthenticationProvider);
    }
}
posted @ 2021-04-02 21:34  当康  阅读(631)  评论(0编辑  收藏  举报