视频教程地址: https://www.bilibili.com/video/BV1kT4y1F7Tc
代码地址: https://gitee.com/crazyliyang/video-teaching

1. 核心配置类 WebSecurityConfigurerAdapter

  1. @Configuration
  2. @EnableGlobalMethodSecurity(prePostEnabled = true) // 注意
  3. public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
  4. @Override
  5. protected void configure(HttpSecurity http) throws Exception {
  6. http
  7. .authorizeRequests() // 配置请求地址的权限
  8. .antMatchers("/demo").permitAll() // 所有用户可访问,无需登录认证, @PermitAll 功能一样
  9. .antMatchers("/config/admin").hasRole("ADMIN") // 需要 ADMIN 角色
  10. .antMatchers("/config/normal").access("hasRole('ROLE_NORMAL')") // 需要 NORMAL 角色。
  11. .anyRequest().authenticated() // 任何请求,访问的用户都需要经过认证
  12. .and()
  13. .formLogin() // 设置 Form 表单登陆
  14. // .loginPage("/login") // 登陆 URL 该地址这里可以自定义配置
  15. .permitAll()
  16. .and()
  17. .logout() // 配置退出相关
  18. // .logoutUrl("/logout") // 退出 URL 该地址这里可以自定义配置
  19. .permitAll();
  20. }
  21. @Override
  22. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  23. auth.
  24. inMemoryAuthentication()// 使用内存中的 InMemoryUserDetailsManager
  25. .passwordEncoder(NoOpPasswordEncoder.getInstance())// 不使用 PasswordEncoder 密码编码器
  26. .withUser("admin").password("admin").roles("ADMIN") // 配置 admin 用户
  27. .and().withUser("user").password("user").roles("NORMAL");// 配置 normal 用户
  28. }
  29. }

如上的注释已经解释和常规的配置什么含义, 下边是总结:

当配置了上述的 WebSecurityConfigurerAdapter Java的配置类之后,我们的应用便具备了如下的功能:
除了“/”,”/home”(首页),”/login”(登录),”/logout”(注销)之外,其他路径都需要认证。
指定“/login”该路径为登录页面,当未认证的用户尝试访问任何受保护的资源时,都会跳转到“/login”。
默认指定“/logout”为注销页面
配置一个内存中的用户认证器,使用admin/admin作为用户名和密码,具有USER角色
防止CSRF攻击

还可以继续配置, 如下:

  1. @Configuration
  2. public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {
  3. @Override
  4. protected void configure(HttpSecurity http) throws Exception {
  5. http
  6. .authorizeRequests()
  7. .antMatchers("/resources/**", "/signup", "/about").permitAll()
  8. .antMatchers("/admin/**").hasRole("ADMIN")
  9. .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
  10. .anyRequest().authenticated()
  11. .and()
  12. .formLogin()
  13. .usernameParameter("username") // 指定登录用户名的参数名
  14. .passwordParameter("password") // 指定登录密码的参数名
  15. .failureForwardUrl("/login?error")
  16. .loginPage("/login")
  17. .permitAll()
  18. .and()
  19. .logout()
  20. .logoutUrl("/logout")
  21. .logoutSuccessUrl("/index")
  22. .permitAll()
  23. .and()
  24. .httpBasic()
  25. .disable();
  26. }
  27. }

其实在后边的自动装配流程原理分析, 和源码剖析会讲的更深入

如果是项目使用了使用JSP 还可以指定自己的登录页面, 登出页面, 如下所示:
SpringBoot 集成 JSP SpringBoot官方是不推荐在SpringBoot中使用jsp的,那么到底可以使用吗? 是可以的
不过需要导入tomcat插件启动项目,不能再用SpringBoot默认tomcat了。这里不拓展了, 因为现在大前端, 前后端分离已经是绝对的主流,这里就不讲这种老技术了.

  1. http.authorizeRequests()
  2. .antMatchers("/login.jsp", "/failer.jsp", "/css/**", "/img/**", "/plugins/**")
  3. .permitAll() //以上指定的资源允许直接访问
  4. .anyRequest() .authenticated() // 其他资源设置拦截
  5. .and()
  6. .formLogin()
  7. .loginPage("/login.jsp") // 指定自定义的登录页面
  8. .loginProcessingUrl("/login")
  9. .successForwardUrl("/index.jsp") // 成功后访问的地址
  10. .failureForwardUrl("/failer.jsp")// 失败后访问的地址
  11. .permitAll()
  12. .and()
  13. .logout()
  14. .logoutUrl("/logout") //登出
  15. .invalidateHttpSession(true)
  16. .logoutSuccessUrl("/login.jsp")
  17. .permitAll()
  18. .and()
  19. .csrf()
  20. .disable();

一个小小的案例说明; 来看下如何配置我们的 SpringSecurity 项目

core-config.png

核心配置类:

  1. /**
  2. *
  3. * 1.基于角色的权限访问控制RBAC(role-based access control)
  4. * 是以角色为中心进行的访问控制,判断当前请求用户 是否具有指定的角色, 是粗粒度的, 不建议采用;
  5. * </p>
  6. * 2.基于资源的权限访问控制RBAC(resource-based access control)
  7. * 是以资源为中心进行的访问控制,是以权限码(资源码) 来判断当前请求的用户是否具有指定的权限码(资源码) 细粒度, 建议采用;
  8. */
  9. @Configuration
  10. @EnableGlobalMethodSecurity(prePostEnabled = true)
  11. public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
  12. @Autowired
  13. private UserService userService;
  14. @Override
  15. protected void configure(HttpSecurity http) throws Exception {
  16. http
  17. .authorizeRequests() // 配置请求地址的权限
  18. .antMatchers("/test01").permitAll() // 所有用户可访问,无需登录认证, @PermitAll 功能一样
  19. // 基于 权限码(资源码)
  20. .antMatchers("/test02").hasAnyAuthority("sys.user.add","") // 含有这里指 权限码(资源码)数组中 任何一个即可
  21. .antMatchers("/test03").access("hasAnyAuthority('sys.user.add')")
  22. .antMatchers("/test04").hasAuthority("sys.user.delete") // 必须含有指定的 权限码(资源码)
  23. .antMatchers("/test05").access("hasAuthority('sys.user.delete')")
  24. // 基于角色
  25. .antMatchers("/test06").hasAnyRole("admin","manager") // 含有这里指 角色数组中 任何一个即可
  26. .antMatchers("/test07").access("hasAnyRole('ROLE_admin')")
  27. .antMatchers("/test08").hasRole("admin") // 必须含有指定的 角色
  28. .antMatchers("/test09").access("hasRole('ROLE_admin')")
  29. .anyRequest().authenticated() // 任何请求,访问的用户都需要经过认证
  30. .and()
  31. .formLogin() // 设置 Form 表单登陆
  32. .permitAll()
  33. .and()
  34. .logout() // 配置退出相关
  35. .permitAll()
  36. .and()
  37. .csrf()
  38. .disable();
  39. }
  40. @Override
  41. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  42. auth
  43. //inMemoryAuthentication()// 使用内存中的 InMemoryUserDetailsManager
  44. .userDetailsService(userService)
  45. .passwordEncoder(NoOpPasswordEncoder.getInstance()); // 不使用 PasswordEncoder 密码编码器
  46. //.withUser("admin").password("admin").roles("ADMIN") // 配置 admin 用户
  47. //.and().withUser("user").password("user").roles("NORMAL");// 配置 normal 用户
  48. }
  49. }

UserService接口 和 UserServiceImpl

  1. public interface UserService extends UserDetailsService {
  2. }
  3. @Service
  4. public class UserServiceImpl implements UserService {
  5. // 模拟数据库
  6. private static Map<String, UserDetails> userDao = new HashMap();
  7. static {
  8. // 权限集合
  9. ArrayList<UserGrantedAuthority> list = new ArrayList<>();
  10. list.add(new UserGrantedAuthority("sys.user.delete"));
  11. list.add(new UserGrantedAuthority("sys.user.add"));
  12. list.add(new UserGrantedAuthority("sys.user.update"));
  13. list.add(new UserGrantedAuthority("sys.user.query"));
  14. list.add(new UserGrantedAuthority("sys.user.query"));
  15. list.add(new UserGrantedAuthority("ROLE_admin"));
  16. userDao.put("user1001", new UserEntity("user1001", "user1001",list));
  17. }
  18. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  19. return userDao.get(username);
  20. }
  21. }

UserEntity 实体类

  1. public class UserEntity implements UserDetails {
  2. private String username;
  3. private String password;
  4. private List<UserGrantedAuthority> userGrantedAuthorityList; // 权限集合
  5. public UserEntity() {}
  6. public UserEntity(String username, String password) {
  7. this.username = username;
  8. this.password = password;
  9. }
  10. public UserEntity(String username, String password, List<UserGrantedAuthority> userGrantedAuthorityList) {
  11. this.username = username;
  12. this.password = password;
  13. this.userGrantedAuthorityList = userGrantedAuthorityList;
  14. }
  15. @Override
  16. public Collection<? extends GrantedAuthority> getAuthorities() {
  17. return userGrantedAuthorityList;
  18. }
  19. // 设置权限
  20. public List<UserGrantedAuthority> getUserGrantedAuthorityList() {
  21. return userGrantedAuthorityList;
  22. }
  23. // 设置权限
  24. public void setUserGrantedAuthorityList(List<UserGrantedAuthority> userGrantedAuthorityList) {
  25. this.userGrantedAuthorityList = userGrantedAuthorityList;
  26. }
  27. @Override
  28. public String getUsername() {
  29. return this.username;
  30. }
  31. @Override
  32. public String getPassword() {
  33. return this.password;
  34. }
  35. @Override
  36. public boolean isAccountNonExpired() {
  37. return true;
  38. }
  39. @Override
  40. public boolean isAccountNonLocked() {
  41. return true;
  42. }
  43. @Override
  44. public boolean isCredentialsNonExpired() {
  45. return true;
  46. }
  47. @Override
  48. public boolean isEnabled() {
  49. return true;
  50. }
  51. }

UserGrantedAuthority 权限对象

public class UserGrantedAuthority implements GrantedAuthority {

    private String grantedAuthorityStr;  // 权限标识符 (资源标识符)

    public UserGrantedAuthority() {}

    public UserGrantedAuthority(String grantedAuthorityStr) {
        this.grantedAuthorityStr = grantedAuthorityStr;
    }

    @Override
    public String getAuthority() {
        return grantedAuthorityStr;
    }

    public String getGrantedAuthorityStr() {
        return grantedAuthorityStr;
    }

    public void setGrantedAuthorityStr(String grantedAuthorityStr) {
        this.grantedAuthorityStr = grantedAuthorityStr;
    }
}

TestControllerWithAnnotation 使用注解的测试类

@RestController
public class TestControllerWithAnnotation {

    // 基于 权限码(资源码)
    @PreAuthorize("hasAuthority('sys.user.add')")
    @GetMapping("/test20")
    public String test20() {
        return "test20 success";
    }

    基于 权限码(资源码)
    @PreAuthorize("hasAnyAuthority('sys.user.add','sys.user.delete')")
    @GetMapping("/test21")
    public String test21() {
        return "test21 success";
    }

    // 基于角色
    @PreAuthorize("hasRole('ROLE_admin')")
    @GetMapping("/test22")
    public String test22() {
        return "test22 success";
    }
    // 基于角色
    @PreAuthorize("hasAnyRole('ROLE_admin','ROLE_manager')")
    @GetMapping("/test23")
    public String test23() {
        return "test23 success";
    }
}

测试看结果 ! 本节完 !

视频教程地址: https://www.bilibili.com/video/BV1kT4y1F7Tc
代码地址: https://gitee.com/crazyliyang/video-teaching