视频教程地址: https://www.bilibili.com/video/BV1kT4y1F7Tc
代码地址: https://gitee.com/crazyliyang/video-teaching
1. 核心配置类 WebSecurityConfigurerAdapter
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) // 注意
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() // 配置请求地址的权限
.antMatchers("/demo").permitAll() // 所有用户可访问,无需登录认证, @PermitAll 功能一样
.antMatchers("/config/admin").hasRole("ADMIN") // 需要 ADMIN 角色
.antMatchers("/config/normal").access("hasRole('ROLE_NORMAL')") // 需要 NORMAL 角色。
.anyRequest().authenticated() // 任何请求,访问的用户都需要经过认证
.and()
.formLogin() // 设置 Form 表单登陆
// .loginPage("/login") // 登陆 URL 该地址这里可以自定义配置
.permitAll()
.and()
.logout() // 配置退出相关
// .logoutUrl("/logout") // 退出 URL 该地址这里可以自定义配置
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.
inMemoryAuthentication()// 使用内存中的 InMemoryUserDetailsManager
.passwordEncoder(NoOpPasswordEncoder.getInstance())// 不使用 PasswordEncoder 密码编码器
.withUser("admin").password("admin").roles("ADMIN") // 配置 admin 用户
.and().withUser("user").password("user").roles("NORMAL");// 配置 normal 用户
}
}
如上的注释已经解释和常规的配置什么含义, 下边是总结:
当配置了上述的 WebSecurityConfigurerAdapter Java的配置类之后,我们的应用便具备了如下的功能:
除了“/”,”/home”(首页),”/login”(登录),”/logout”(注销)之外,其他路径都需要认证。
指定“/login”该路径为登录页面,当未认证的用户尝试访问任何受保护的资源时,都会跳转到“/login”。
默认指定“/logout”为注销页面
配置一个内存中的用户认证器,使用admin/admin作为用户名和密码,具有USER角色
防止CSRF攻击
还可以继续配置, 如下:
@Configuration
public class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**", "/signup", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.anyRequest().authenticated()
.and()
.formLogin()
.usernameParameter("username") // 指定登录用户名的参数名
.passwordParameter("password") // 指定登录密码的参数名
.failureForwardUrl("/login?error")
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/index")
.permitAll()
.and()
.httpBasic()
.disable();
}
}
其实在后边的自动装配流程原理分析, 和源码剖析会讲的更深入
如果是项目使用了使用JSP 还可以指定自己的登录页面, 登出页面, 如下所示:
SpringBoot 集成 JSP SpringBoot官方是不推荐在SpringBoot中使用jsp的,那么到底可以使用吗? 是可以的
不过需要导入tomcat插件启动项目,不能再用SpringBoot默认tomcat了。这里不拓展了, 因为现在大前端, 前后端分离已经是绝对的主流,这里就不讲这种老技术了.
http.authorizeRequests()
.antMatchers("/login.jsp", "/failer.jsp", "/css/**", "/img/**", "/plugins/**")
.permitAll() //以上指定的资源允许直接访问
.anyRequest() .authenticated() // 其他资源设置拦截
.and()
.formLogin()
.loginPage("/login.jsp") // 指定自定义的登录页面
.loginProcessingUrl("/login")
.successForwardUrl("/index.jsp") // 成功后访问的地址
.failureForwardUrl("/failer.jsp")// 失败后访问的地址
.permitAll()
.and()
.logout()
.logoutUrl("/logout") //登出
.invalidateHttpSession(true)
.logoutSuccessUrl("/login.jsp")
.permitAll()
.and()
.csrf()
.disable();
一个小小的案例说明; 来看下如何配置我们的 SpringSecurity 项目
核心配置类:
/**
*
* 1.基于角色的权限访问控制RBAC(role-based access control)
* 是以角色为中心进行的访问控制,判断当前请求用户 是否具有指定的角色, 是粗粒度的, 不建议采用;
* </p>
* 2.基于资源的权限访问控制RBAC(resource-based access control)
* 是以资源为中心进行的访问控制,是以权限码(资源码) 来判断当前请求的用户是否具有指定的权限码(资源码) 细粒度, 建议采用;
*/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() // 配置请求地址的权限
.antMatchers("/test01").permitAll() // 所有用户可访问,无需登录认证, @PermitAll 功能一样
// 基于 权限码(资源码)
.antMatchers("/test02").hasAnyAuthority("sys.user.add","") // 含有这里指 权限码(资源码)数组中 任何一个即可
.antMatchers("/test03").access("hasAnyAuthority('sys.user.add')")
.antMatchers("/test04").hasAuthority("sys.user.delete") // 必须含有指定的 权限码(资源码)
.antMatchers("/test05").access("hasAuthority('sys.user.delete')")
// 基于角色
.antMatchers("/test06").hasAnyRole("admin","manager") // 含有这里指 角色数组中 任何一个即可
.antMatchers("/test07").access("hasAnyRole('ROLE_admin')")
.antMatchers("/test08").hasRole("admin") // 必须含有指定的 角色
.antMatchers("/test09").access("hasRole('ROLE_admin')")
.anyRequest().authenticated() // 任何请求,访问的用户都需要经过认证
.and()
.formLogin() // 设置 Form 表单登陆
.permitAll()
.and()
.logout() // 配置退出相关
.permitAll()
.and()
.csrf()
.disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
//inMemoryAuthentication()// 使用内存中的 InMemoryUserDetailsManager
.userDetailsService(userService)
.passwordEncoder(NoOpPasswordEncoder.getInstance()); // 不使用 PasswordEncoder 密码编码器
//.withUser("admin").password("admin").roles("ADMIN") // 配置 admin 用户
//.and().withUser("user").password("user").roles("NORMAL");// 配置 normal 用户
}
}
UserService接口 和 UserServiceImpl
public interface UserService extends UserDetailsService {
}
@Service
public class UserServiceImpl implements UserService {
// 模拟数据库
private static Map<String, UserDetails> userDao = new HashMap();
static {
// 权限集合
ArrayList<UserGrantedAuthority> list = new ArrayList<>();
list.add(new UserGrantedAuthority("sys.user.delete"));
list.add(new UserGrantedAuthority("sys.user.add"));
list.add(new UserGrantedAuthority("sys.user.update"));
list.add(new UserGrantedAuthority("sys.user.query"));
list.add(new UserGrantedAuthority("sys.user.query"));
list.add(new UserGrantedAuthority("ROLE_admin"));
userDao.put("user1001", new UserEntity("user1001", "user1001",list));
}
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userDao.get(username);
}
}
UserEntity 实体类
public class UserEntity implements UserDetails {
private String username;
private String password;
private List<UserGrantedAuthority> userGrantedAuthorityList; // 权限集合
public UserEntity() {}
public UserEntity(String username, String password) {
this.username = username;
this.password = password;
}
public UserEntity(String username, String password, List<UserGrantedAuthority> userGrantedAuthorityList) {
this.username = username;
this.password = password;
this.userGrantedAuthorityList = userGrantedAuthorityList;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return userGrantedAuthorityList;
}
// 设置权限
public List<UserGrantedAuthority> getUserGrantedAuthorityList() {
return userGrantedAuthorityList;
}
// 设置权限
public void setUserGrantedAuthorityList(List<UserGrantedAuthority> userGrantedAuthorityList) {
this.userGrantedAuthorityList = userGrantedAuthorityList;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
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