SpringSecurity--基于数据库的认证

1. 设计数据表

一共三张表:分别是用户表user、角色表role和关联的中间表user_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;

INSERT INTO `role` VALUES ('1', 'ROLE_dba', '数据库管理员');
INSERT INTO `role` VALUES ('2', 'ROLE_admin', '系统管理员');
INSERT INTO `role` VALUES ('3', 'ROLE_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;

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');

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;

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');

2. 创建项目,添加依赖

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</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>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>

3. 配置数据库

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql:///springboot

4. 创建实体类

public class Role {
    private Integer id;
    private String name;
    private String nameZh;
}
public class User implements UserDetails {
    
    private Integer id;
    private String username;
    private String password;
    private Boolean enabled;
    private Boolean locked;
    private List<Role> 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;
    }
//省略getter和setter
}
  • 用户实体类需要实现UserDetails接口,并实现接口中的7个方法:

    方法名 解释
    getAuthorities() 获取当前用户对象所具有的角色信息
    getPassword() 获取当前用户对象的密码
    getUsername() 获取当前用户对象的用户名
    isAccountNonExpired() 当前账户是否未过期
    isAccountNonLocked() 当前账户是否未锁定
    isCredentialsNonExpired() 当前账户密码是否过期
    isEnabled() 当前账户是否可用
  • 用户根据实际情况设置这7个方法的返回值。因为默认情况下不需要开发者自己进行密码角色等信息的对比,开发者只需要提供相关信息即可,例如getPassword()方法返回的密码和用户输入的登录密码不匹配,会自动抛出BadCredentialsException异常,isAccountNonExpired()方法返回了false,会自动抛出AccountExpiredException异常,因此对开发者而言,只需要按照数据库中的数据在这里返回响应配置即可。

  • getAuthorities()方法用来获取当前用户所具有的角色信息,本案例中,用户所具有的角色存储在roles属性中,因此该方法直接遍历roles属性,然后构造SimpleGrantedAuthority集合并返回。

5. 创建UserService

@Service
public class UserService implements UserDetailsService {

    @Autowired
    UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userMapper.loadUserByUsername(username);
        if(user == null){
            throw new UsernameNotFoundException("账户不存在!");
        }
        user.setRoles(userMapper.getUserRolesByUid(user.getId()));
        return user;
    }
}
  • 定义UserService实现UserDetailsService接口,并实现该接口中的loadUserByUsername方法,该方法的参数就是登录时输入的用户名,通过用户名去数据库查找用户,如果没有查找到用户,就抛出一个账户不存在的异常,如果查找到了用户,就继续查找该用户所具有的角色信息,并将获取到的user对象返回,再由系统提供的DaoAuthenticationProvider类去比对密码是否正确。
  • loadUesrByUsername方法将在用户登录时自动调用。

创建UserMapper和UserMapper.xml

@Mapper
public interface UserMapper {

    User loadUserByUsername(String username);

    List<Role> getUserRolesByUid(Integer id);
}
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="pers.zhang.demo.mapper.UserMapper">
    <select id="loadUserByUsername" resultType="pers.zhang.demo.pojo.User">
        select *
        from user
        where username = #{username}
    </select>
    <select id="getUserRolesByUid" resultType="pers.zhang.demo.pojo.Role">
        select *
        from role r,user_role ur
        where r.id = ur.rid and ur.uid = #{id}
    </select>
</mapper>

6. 配置Spring Security

@Configuration
public class WebSecurityConfig2 extends WebSecurityConfigurerAdapter {

    @Autowired
    UserService userService;

    @Bean
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("admin")
                .antMatchers("/db/**").hasRole("dba")
                .antMatchers("/user/**").hasRole("user")
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginProcessingUrl("/login").permitAll()
                .and()
                .csrf().disable();
    }
}

7. 创建Controler

@RestController
public class HelloController {

    @GetMapping("/admin/hello")
    public String admin(){
        return "hello admin!";
    }

    @GetMapping("/user/hello")
    public String user(){
        return "hello user!";
    }

    @GetMapping("/db/hello")
    public String dba(){
        return "hello dba!";
    }
}

8. 测试

密码均为:123

  • root用户:可以访问"/admin/hello"和"/db/hello"接口
    在这里插入图片描述
    在这里插入图片描述

  • admin用户:可以访问"/admin/hello"接口
    在这里插入图片描述

  • sang用户:可以访问"/user/hello"接口
    在这里插入图片描述

发布了716 篇原创文章 · 获赞 2079 · 访问量 26万+

猜你喜欢

转载自blog.csdn.net/cold___play/article/details/104219588