美文网首页
spring security单体应用实践

spring security单体应用实践

作者: 粑粑八成 | 来源:发表于2021-04-10 09:11 被阅读0次

    搭建demo

    1. 如果不设置,默认账号user,密码会生成uuid打印在控制台中
    2. 可以在application.yml中设置
    spring:
      security:
        user:
          name: 123
          password: 123
    

    使用内存信息进行认证

    1. 继承WebSecurityConfigurerAdapter类,定义自己的配置文件
    2. 增加Configuration和EnableWebSecurity注解
    3. 重载configure(AuthenticationManagerBuilder auth)方法
    4. 密码必须加密,使用BCryptPasswordEncoder进行hash加密
    5. EnableGlobalMethodSecurity注解可以开启方法级别的权限控制
    /**
     * @EnableGlobalMethodSecurity: 启用方法级别的认证
     *    prePostEnabled: boolean 默认false
     *      true表示可以使用@PreAuthorize和@PostAuthorize
     */
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
    
      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        PasswordEncoder passwordEncoder = passwordEncoder();
        // 定义两个角色,normal, admin
        auth.inMemoryAuthentication().withUser("zhangsan").password(passwordEncoder.encode("123456"))
            .roles("normal");
    
        auth.inMemoryAuthentication().withUser("lisi").password(passwordEncoder.encode("123456"))
            .roles("normal");
    
        auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder.encode("admin"))
            .roles("admin", "normal");
      }
    
      @Bean
      public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
      }
    }
    
    
    @RestController
    @RequestMapping("/hello")
    public class HelloWorldController {
    
      @GetMapping("/world")
      public String sayHello() {
        return "Hello Srping security";
      }
    
      @GetMapping("/user")
      @PreAuthorize(value = "hasAnyRole('admin', 'mormal')")
      public String helloCommonUser() {
        return "Hello all roles";
      }
    
      @GetMapping("/admin")
      @PreAuthorize(value = "hasAnyRole('admin')")
      public String helloAdminUser() {
        return "Hello admin roles";
      }
    }
    
    

    基于jdbc的用户认证

    1. 从数据库中获取用户信息(用户名,密码,角色)
    2. 在spring security中,用户信息的表示类是UserDetails(接口)
    3. User implements UserDetails,spring security使用User 对象进行认证
    public interface UserDetails extends Serializable {
    // 用户角色
    Collection<? extends GrantedAuthority> getAuthorities(); // 角色集合
    
    String getPassword();
    
    String getUsername();
    
    boolean isAccountNonExpired(); // 账号是否过期
    
    boolean isAccountNonLocked(); // 账号是否锁定
    
    boolean isCredentialsNonExpired(); // 证书是否过期
    
    boolean isEnabled(); // 账号是否启用
    }
    
    1. 实现UserDetailsService接口,根据username查询数据库得到User对象
    @Component("MyUserDetailService")
    public class MyUserDetailService implements UserDetailsService {
    
      @Resource
      private UserInfoMapper userInfoMapper;
    
      @Override
      public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        User user = null;
        UserInfo userInfo = userInfoMapper.findByUserName(userName);
        if (userInfo != null) {
          ArrayList<GrantedAuthority> list = new ArrayList<>();
    
          // 角色必须以ROLE_开头
          GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_" + userInfo.getRole());
          list.add(authority);
    
          user = new User(userInfo.getUsername(), userInfo.getPassword(), list);
        }
    
        return user;
      }
    }
    
    1. 修改配置类
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
    
      @Autowired
      @Qualifier("MyUserDetailService")
      private UserDetailsService userDetailsService;
    
      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // ------------ 基于数据库的验证-------------
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    // ------------基于内存的验证----------------------
    //    PasswordEncoder passwordEncoder = passwordEncoder();
    //    // 定义两个角色,normal, admin
    //    auth.inMemoryAuthentication().withUser("zhangsan").password(passwordEncoder.encode("123456"))
    //        .roles("normal");
    //
    //    auth.inMemoryAuthentication().withUser("lisi").password(passwordEncoder.encode("123456"))
    //        .roles("normal");
    //
    //    auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder.encode("admin"))
    //        .roles("admin", "normal");
      }
    
    //  @Bean
    //  public PasswordEncoder passwordEncoder() {
    //    return new BCryptPasswordEncoder();
    //  }
    }
    

    认证和授权

    • authentication:认证,认证访问者是谁。用户是不是当前系统的有效用户
    • authorization:授权,访问者能做什么

    RBAC模型

    • role-based access control 基于角色的权限控制
    • 权限:对资源的操作
    • 角色:权限的集合,用户通过获得角色来获得权限

    RBAC表设计

    1. 用户表
    2. 角色表
    3. 用户角色表(多对对关联表)
    4. 权限表
    5. 角色权限表(多对对关联表)

    使用DetailsManager创建用户(manager提供了对用户的增删改查)

    • JdbcUserDetailsManager和InMemoryUserDetailsManager基本相同
    • 使用JdbcUserDetailsManager配置数据源,可以直接写进数据库,使用jdbcTemplate操作数据库
    • 框架默认的角色和用户表 org\springframework\security\spring-security-core\5.4.5\spring-security-core-5.4.5.jar!\org\springframework\security\core\userdetails\jdbc\users.ddl
    @Configuration
    public class ApplicationConfig {
    
      @Bean
      public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
      }
    
      @Autowired
      private DataSource dataSource;
    
      @Bean
      public UserDetailsService getUserDetailsService() {
        System.out.println("dataSource ---" + dataSource);
        PasswordEncoder encoder = passwordEncoder();
    //    InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        JdbcUserDetailsManager manager = new JdbcUserDetailsManager(dataSource);
    
        // 如果数据库中已经存在该用户,则不创建
        if (!manager.userExists("admin")) {
          manager.createUser(
              User.withUsername("admin")
                  .password(encoder.encode("admin"))
                  .roles("ADMIN", "USER").build());
        }
    
        if (!manager.userExists("zs")) {
          manager.createUser(
              User.withUsername("zs")
                  .password(encoder.encode("123"))
                  .roles("USER").build());
        }
    
        return manager;
      }
    
    }
    

    自定义角色用户表

    CREATE TABLE sys_user (
        id BIGINT AUTO_INCREMENT PRIMARY KEY,
        username VARCHAR ( 100 ),
        password VARCHAR ( 100 ),
        realname VARCHAR ( 200 ),
        is_enabled TINYINT,
        is_locked TINYINT,
        is_credential_expired TINYINT,
        creation_time date,
        login_time date 
    );
    
    CREATE TABLE sys_role (
        id BIGINT AUTO_INCREMENT PRIMARY KEY,
        rolename VARCHAR ( 255 ),
      description VARCHAR ( 255 )
    );
    
    CREATE TABLE sys_user_role (
        id BIGINT AUTO_INCREMENT PRIMARY KEY,
        user_id BIGINT,
        role_id BIGINT
    );
    
    

    数据初始化

    账号 密码 角色
    zs 123 USER
    lisi 123 READ
    admin admin ADMIN、USER

    集成mybatis,SysUser implements UserDetails 自定义用户类实现userDetails接口,自定义登录界面,自定义登录失败界面,实现接口权限控制

     public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        SysUser sysUser = sysUserMapper.selectSysUser(userName);
        if (sysUser != null) {
          List<SysRole> roles = sysRoleMapper.selectRoleByUser(sysUser.getId());
    
          List<GrantedAuthority> grantedAuthorities = roles.stream().map(SysRole::getRolename)
              .map(role -> new SimpleGrantedAuthority("ROLE_" + role)).collect(
                  Collectors.toList());
          sysUser.setAuthorities(grantedAuthorities);
        }
    
        return sysUser;
      }
    
    // 重定向到index.html
    @Controller
    public class IndexController {
    
      @GetMapping("/index")
      public String toIndex() {
        return "forward:/index.html";
      }
    
    }
    
    
    <!--index.html-->
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <body>
    <p>验证访问</p>
    <a href="/hello/user">验证zs</a>
    <a href="/hello/read">验证lisi</a>
    <a href="/hello/admin">验证admin</a>
    <a href="/logout">退出</a>
    </body>
    </html>
    
    <!--mylogin.html-->
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>自定义登录界面</title>
    </head>
    <body>
    <p>自定义登录界面</p>
    <!-- login接口是spring security自带 -->
    <form action="/login" method="post"> 
      <div>用户名:<input type="text" name="username"></div>
      <div>密码:<input type="password" name="password"></div>
      <input type="submit" value="登录">
    </form>
    </body>
    </html>
    
    
     @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
      }
    
     @Override
      protected void configure(HttpSecurity http) throws Exception {
    //    super.configure(http);
        http.authorizeRequests()
            // inde是跳转的controller,mylogin自定义登录界面,/login是spring security UsernamePasswordAuthenticationFilter 默认的登录认证接口,可以随便写
            .antMatchers("/index", "/mylogin.html", "/login", "/error.html").permitAll()
            .antMatchers("/hello/user").hasAnyRole("USER")
            .antMatchers("/hello/read").hasAnyRole("READ")
            .antMatchers("/hello/admin").hasAnyRole("ADMIN")
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .loginPage("/mylogin.html") // 自定义登录界面
            .loginProcessingUrl("/login") // spring security UsernamePasswordAuthenticationFilter 默认的登录认证接口,可以随便写
            .failureUrl("/error.html") // 登陆错误页面
            .and()
            .csrf().disable(); // 禁用csrf token,开发中用oauth,CsrfFilter,通过对比cookie/session(CsrfToken类型)和header中的token(actualToken)进行校验
      }
    

    微服务权限案例

    相关文章

      网友评论

          本文标题:spring security单体应用实践

          本文链接:https://www.haomeiwen.com/subject/iynehltx.html