简介

一般来说,Web应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分,这两点也是SpringSecurity重要核心功能

  • 用户认证。验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码,系统通过校验用户名和密码来完成认证过程。通俗点就是系统认为用户是否能登录
  • 用户授权。验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。通俗点讲就是系统判断用户是否有权限去做某些事情

和Shiro对比

  • SpringSecurity
    • 和Spring无缝整合
    • 全面的权限控制
    • 专门为Web开发设计
      • 旧版本不能脱离Web环境使用
      • 新版本对整个框架进行了分层抽取,分成了核心模块和Web模块。单独引入核心模块就可以脱离Web环境
    • 重量级
  • Shiro
    • Apache旗下的轻量级权限控制框架
    • 轻量级,主张把复杂的事情变简单。针对性能有更高要求的互联网应用有更好表现
    • 通用性
      • 好处:不限于Web环境,可以脱离Web环境使用
      • 缺陷:在Web环境下一些特定的需求需要手动编写代码定制

一般来说,常见的安全管理技术栈组合是 SSM+ShiroSpringBoot/SpringCloud+SpringSecurity

简单示例

新建一个Springboot项目,引入SpringSecurity依赖

  1. <!--spring security-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-security</artifactId>
  5. </dependency>

新建一个Controller类,测试一下

  1. @GetMapping(value = "/hello")
  2. public Result test(){
  3. return Result.success("hello");
  4. }

浏览器请求,发现和以前不一样了
image.png
需要账号密码登录,默认的账号是user,密码是项目启动时给出的
image.png
可以看到这里的密码是b99d0223-4009-4009-9a2c-1c04470cc1f1,登录后就可以成功获取请求内容
但是一般开发时,用户名和密码都是自己从数据库查的,这里就用到下面的两个重要接口

两个重要的接口

UserDetailService接口:查询数据库用户名和密码

  • 创建类继承UsernamePasswordAuthorizationFilter,重写三个方法
  • 创建类实现UserDetailService,编写查询数据过程,返回User对象买这个User对象是安全框架提供的对象

PasswordEncode接口:数据加密接口,用于返回User对象里面密码加密

Web权限方案

设置账号、密码

这里有三种方法实现,在application.yml文件中配置、通过配置类配置、自定义编写实现类,以下依次实现

在application.yml中配置

  1. spring:
  2. security:
  3. user:
  4. name: myuser
  5. password: 123456

配置即可

通过配置类

  1. @Configuration
  2. public class SecuritySimpleConfig extends WebSecurityConfigurerAdapter {
  3. @Override
  4. protected void configure(AuthenticationManagerBuilder auth) throws Exception{
  5. //密码加密
  6. BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
  7. String password = encoder.encode("123456");
  8. auth.inMemoryAuthentication().withUser("bob").password(password).roles("");
  9. }
  10. @Bean
  11. PasswordEncoder passwordEncoder(){
  12. return new BCryptPasswordEncoder();
  13. }
  14. }

配置类继承WebSecurityConfigurerAdapter类,重写configure方法,在方法中设置用户名和密码并对密码加密

自定义实现类

  • 创建配置类,设置使用哪个userDetailService实现类
  • 编写实现类,返回User对象,User对象有用户名、密码、操作权限

    1. @Configuration
    2. public class SecurityComplexConfig extends WebSecurityConfigurerAdapter {
    3. @Autowired
    4. private UserDetailsService userDetailsService;
    5. @Override
    6. protected void configure(AuthenticationManagerBuilder auth) throws Exception{
    7. auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    8. }
    9. @Bean
    10. PasswordEncoder passwordEncoder(){
    11. return new BCryptPasswordEncoder();
    12. }
    13. }
    1. @Service("userDetailsService")
    2. public class WebUserServiceImpl implements UserDetailsService {
    3. @Override
    4. public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
    5. List<GrantedAuthority> role = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
    6. return new User("mary", new BCryptPasswordEncoder().encode("123456"), role);
    7. }
    8. }

    这里没连数据库,仅做演示

使用

注解使用

  • @Secured

用户具有某个角色,可以访问方法
使用时,需要现在启动类或配置类上开启注解

  1. @EnableGlobalMethodSecurity(securedEnabled = true)

在controller的方法上使用注解,设置角色

  1. @Secured({"ROLE_one","ROLE_two"})

可以设置多个角色,并且在角色名称前加前缀“ROLE_”,即如果角色是“one”,则这里值是“ROLE_one”

  • @PreAuthorize

使用时,需要现在启动类或配置类上开启注解

  1. @EnableGlobalMethodSecurity(prePostEnabled = true)

在controller的方法上使用注解,四个用法选一个

  1. @PreAuthorize("hasRole('ROLE_admin')")
  2. @PreAuthorize("hasAnyRole('ROLE_admin,Role_one')")
  3. @PreAuthorize("hasAuthority('menu:system')")
  4. @PreAuthorize("hasAnyAuthority('menu:system,menu:one')")
  • @PostAuthorize

这个注解用的不多,作用是在方法执行后再进行权限验证,适合验证带有返回值的权限
先开始注解

  1. @EnableGlobalMethodSecurity(prePostEnabled = true)

在controller的方法上使用注解

  1. @PostAuthorize("hasAuthority('menu:system')")
  • @PreFilter

进入控制器之前对数据进行过滤