spring security单体应用实践

2021-04-10  本文已影响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();
//  }
}

认证和授权

RBAC模型

RBAC表设计

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

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

@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)进行校验
  }

微服务权限案例

上一篇下一篇

猜你喜欢

热点阅读