Spring Boot整合
1、RBAC
权限模型
RBAC模型(Role-Based Access Control:基于角色的访问控制)
在RBAC模型里面,有3个基础组成部分,分别是:用户、角色和权限,它们之间的关系如下图所示
sql
SELECT * FROM sec_permission;
SELECT * FROM sec_role_permission ;
SELECT * FROM sec_role;
SELECT * FROM sec_user_role;
SELECT * FROM sec_user;
2、启动器依赖引入
啥配置也没做,啥类也没写。只是增加了一个启动器依赖
XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
重新访问首页:http://localhost:8080/
3、用户名密码
默认:
用户名默认:user
密码在服务启动时打印在了控制台
自定义:当然我们也可以通过application.yml指定配置用户名密码
security.user.name 指定默认的用户名,默认为user.
security.user.password 默认的用户密码.
javaspring: security: user: name: admin password: admin
关闭security验证:
java@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().permitAll().and().logout().permitAll();//配置不需要登录验证 } }
WebSecurityConfigurerAdapter是由Spring Security提供的Web应用安全配置的适配器
WebSecurityConfigurerAdapter
是一个适配器类,允许开发者通过重写特定的方法来自定义其 Web 安全配置
创建一个配置类WebSecurityConfig
继承**WebSecurityConfigurerAdapter
** 这个抽象类并重写configure(HttpSecurity http)
方法,可以精确地定义哪些URL可以由哪些角色访问。
java
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() // 表单方式
.and()
.authorizeRequests() // 授权配置
.anyRequest().authenticated(); //所有未匹配的请求都需要用户进行身份验证
}
}
Spring Security提供了这种链式的方法调用。上面配置指定了认证方式为表单登录,并且所有请求都需要进行认证。
**HttpSecurity
**是 Spring Security 中用于构建安全配置的一个类。通过该类,开发者可以配置许多与 HTTP 安全相关的选项,如认证、授权、CORS、CSRF 保护等
.formLogin()
是HttpSecurity
类的一个方法,用于启用基于表单的身份验证。当你调用这个方法时,Spring Security 会自动配置登录表单的相关设置,如登录页面的 URL、登录成功和失败的处理等。你可以进一步定制这些设置,以适应你的应用程序需求。
http.authorizeRequests()
是HttpSecurity
类的一个方法,用于定义 URL 的访问权限。通过该方法,你可以指定哪些 URL 需要特定的角色或权限才能访问,哪些 URL 可以公开访问等。
.anyRequest().authenticated()
表示所有未匹配的请求都需要用户进行身份验证。
4、基于数据库的登录认证
Spring Security支持通过实现UserDetailsService接口的方式来提供用户认证授权信息。主要功能:根据用户名查询用户信息
java
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserDao userDao;
@Autowired
private RoleDao roleDao;
@Autowired
private PermissionDao permissionDao;
@Resource
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//通过用户名从数据库获取用户信息
User user = userDao.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("未找到用户信息 : " + username));
//定义权限列表
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("a"));
authorities.add(new SimpleGrantedAuthority("b"));
authorities.add(new SimpleGrantedAuthority("c"));
//返回spring security的User对象
//user.getPassword() 数据库中的密码已经是密文存储
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
}
}
返回值类型是UserDetails
,我们可以直接使用Spring Security提供的UserDetails
接口实现类org.springframework.security.core.userdetails.User
5、授权GrantedAuthority
GrantedAuthority
则表示用户验证通过后被授予的权限。
SimpleGrantedAuthority
SimpleGrantedAuthority
是默认的授权实现,它只存储权限(存储授予Authentication
对象的权限的String
表示形式),是一种简易的授权实现。
GrantedAuthority:直译"授予权限"
Authentication:直译"验证"
给我的感觉就是权限就是一个字符串,难道什么样的字符串都行吗?为啥定义的这么模糊
那我们就姑且给他"a","b","c"。。看看它怎么说
AuthorityUtils:此类一般用于UserDetailsService的实现类中的loadUserByUsername方法
作用为给user账户添加一个或多个权限,用逗号分隔,底层调用的是createAuthorityList 方法,唯一区别在于此方法把所有的权限包含进一个字符串参数中,只不过用逗号分隔。
java@Service public class UserDetailsServiceImpl implements UserDetailsService{ @Autowired PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //比较密码 String pass=passwordEncoder.encode("123");//加密 return new User(username,pass,AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal")); } }
createAuthorityList
将权限转换为List,如
java@Service public class UserDetailsServiceImpl implements UserDetailsService{ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { List<GrantedAuthority> list=AuthorityUtils.createAuthorityList("admin","normal");//一个权限一个参数 return new User(username,pass,list); } }
1
6、配置类中配置
实际项目中我们不会把密码明文存储在数据库中。只需要使用把BCryptPasswordEncoder对象注入Spring容器中,SpringSecurity就会使用该PasswordEncoder来进行密码校验
Spring Security实现的BCryptPasswordEncoder
已经足够强大,它对相同的密码进行加密后可以生成不同的结果
java
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
//使用默认的BCryptPasswordEncoder加密方案
return new BCryptPasswordEncoder();
}
/**
* 配置用户详细信息的服务和密码编码器
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//数据库读取的用户进行身份认证
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() // 表单方式
.and()
.authorizeRequests() // 授权配置
.anyRequest().authenticated(); //所有未匹配的请求都需要用户进行身份验证
}
}
Spring Security中的BCryptPasswordEncoder方法采用SHA-256 +随机盐+密钥对密码进行加密。SHA系列是Hash算法,不是加密算法,使用加密算法意味着可以解密(这个与编码/解码一样),但是采用Hash处理,其过程是不可逆的。
1)加密(encode):注册用户时,使用SHA-256+随机盐+密钥把用户输入的密码进行hash处理,得到密码的hash值,然后将其存入数据库中。
2)密码匹配(matches):用户登录时,密码匹配阶段并没有进行密码解密(因为密码经过Hash处理,是不可逆的),而是使用相同的算法把用户输入的密码进行hash处理,得到密码的hash值,然后将其与从数据库中查询到的密码hash值进行比较。如果两者相同,说明用户输入的密码正确。
再次访问接口:http://127.0.0.1:8089/hello
使用账号密码登录 admin/123456
7、权限控制
Spring Security
支持方法级别的权限控制。在此机制上,我们可以在任意层的任意方法上加入权限注解,加入注解的方法将自动被Spring Security
保护起来,仅仅允许特定的用户访问,从而还到权限控制的目的
@PreAuthorize() 该注解用于方法前验证权限
java//使用权限注解标明只有拥有"admin"权限的人才能访问: @PreAuthorize("hasAuthority('admin')")
java@RestController public class HelloController { @RequestMapping("/hello") public String sayHello() { return "hello"; } @RequestMapping("/a") @PreAuthorize("hasAuthority('a')") public String sayA() { return "aaaaa"; } @RequestMapping("/d") @PreAuthorize("hasAuthority('d')") public String sayB() { return "ddddd"; } }
Spring Security
默认是禁用注解的,要想开启注解,要在继承WebSecurityConfigurerAdapter
的类加@EnableGlobalMethodSecurity()注解,并在该类中将AuthenticationManager
定义为Bean。说实话我没有注入AuthenticationManager这个bean的时候,也做到了权限校验。。这到底有啥用?
java
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
//使用默认的BCryptPasswordEncoder加密方案
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 配置用户详细信息的服务和密码编码器
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//数据库读取的用户进行身份认证
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() // 表单方式
.and()
.authorizeRequests() // 授权配置
.anyRequest().authenticated(); //所有未匹配的请求都需要用户进行身份验证
}
}
我们看到
@EnableGlobalMethodSecurity
分别有prePostEnabled 、securedEnabled、jsr250Enabled
三个字段,其中每个字段代码一种注解支持,默认为false,true为开启
。
重新登录访问。记得之前我随便给的权限字符串a,b,c。。。访问a是没问题的
访问d会返回403错误码。
自定义权限不足处理器来处理权限不足时候的操作
8、Session管理
用户登录成功后,信息保存在服务器Session中。如Tomcat
登录后,可以看到cookie中存储了JSESSIONID的cookie。
Session超时设置
如可以设置session有效期为1小时
server:
session:
timeout: 3600
这时候,就涉及到一个session共享
当应用集群部署的时候,用户在A应用上登录认证了,后续通过负载均衡可能会把请求发送到B应用,而B应用服务器上并没有与该请求匹配的认证Session信息,所以用户就需要重新进行认证
Spring Security默认的退出登录URL为/logout
Spring Security OAuth2
1、什么是OAuth
OAuth是一种用来规范令牌(Token)发放的授权机制,主要包含了四种授权模式:授权码模式、简化模式、密码模式和客户端模式
OAuth相关的名词
-
Third-party application 第三方应用程序,比如这里的虎牙直播;
-
HTTP service HTTP服务提供商,比如这里的QQ(腾讯);
-
Resource Owner 资源所有者,就是QQ的所有人,你;
-
User Agent 用户代理,这里指浏览器;
-
Authorization server 认证服务器,这里指QQ提供的第三方登录服务;
-
Resource server 资源服务器,这里指虎牙直播提供的服务,比如高清直播,弹幕发送等(需要认证后才能使用)。
Spring Security OAuth2主要包含认证服务器和资源服务器这两大块的实现:
认证服务器主要包含了四种授权模式的实现和Token的生成与存储
资源服务器主要是在Spring Security的过滤器链上加了OAuth2AuthenticationProcessingFilter过滤器,即使用OAuth2协议发放令牌认证的方式来保护我们的资源
2、认证授权服务器
创建认证服务器很简单,只需要在Spring Security的配置类上使用@EnableAuthorizationServer
注解标注即可
使用
@EnableAuthorizationServer
注解,在应用中自动开启和配置 Spring Security OAuth 的授权服务组件。
@EnableAuthorizationServer
注解主要是导入两个配置类,分别是:
AuthorizationServerEndpointsConfiguration
,这个配置类主要配置授权端点,获取token的端点。大家就把对应的端点想象成controller即可,在这个controller下开放了若干个@RequestMapping,比如常见的有:/oauth/authorize(授权路径)
,/oauth/token(获取token)
等AuthorizationServerSecurityConfiguration
,主要是做spring-security的安全配置
3、资源服务器
资源服务器的配置也很简单,只需要在配置类上使用@EnableResourceServer
注解标注即可:
通过资源服务器来保护我们指定的资源,必须在获取授权认证的时候才能访问。在SpringBoot当中,我们可以通过@EnableResourceServer
注解来开启此功能。
java
@Configuration
@EnableResourceServer
public class ResourceConfigure extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and().authorizeRequests().antMatchers("/free/**").permitAll().and()
.authorizeRequests().anyRequest().authenticated()
.and().formLogin().permitAll();//必须认证过后才可以访问
}
}