Spirng Security知识点整理

Posted 大忽悠爱忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spirng Security知识点整理相关的知识,希望对你有一定的参考价值。

Spirng Security


案例

新建工程,引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>SpringSecurity</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

<!--    父工程-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.4</version>
    </parent>

  <dependencies>

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-security</artifactId>
      </dependency>

      <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <optional>true</optional>
      </dependency>

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-devtools</artifactId>
          <scope>runtime</scope>
          <optional>true</optional>
      </dependency>
      
  </dependencies>
    
</project>

创建启动项和controller层

@SpringBootApplication
public class main
{
    public static void main(String[] args) {
        SpringApplication.run(main.class,args);
    }
}
@RestController
public class HelloController
{
    @GetMapping("/hello")
    public String hello()
    {
        return "hello spring security";
    }
}


启动项目

启动日志会打印一个通过UUID随机生成的密码

访问controller,首先请求会被安全框架的aop机制拦截,要求使用用户名和密码验证登录

默认的用户名和密码为:

用户名: user

密码: 日志打印生成的uuid


自定义用户名和密码

配置文件中设置用户名和密码

spring:
  security:
    user:
      name: 大忽悠
      password: 123456

对应的绑定配置文件的类,如下:

@ConfigurationProperties(
    prefix = "spring.security"
)
public class SecurityProperties {
    public static final int BASIC_AUTH_ORDER = 2147483642;
    public static final int IGNORED_ORDER = -2147483648;
    public static final int DEFAULT_FILTER_ORDER = -100;
    private final SecurityProperties.Filter filter = new SecurityProperties.Filter();
    private final SecurityProperties.User user = new SecurityProperties.User();

    public SecurityProperties() {
    }

    public SecurityProperties.User getUser() {
        return this.user;
    }

    public SecurityProperties.Filter getFilter() {
        return this.filter;
    }

    public static class User {
        private String name = "user";
        private String password = UUID.randomUUID().toString();
        private List<String> roles = new ArrayList();
        private boolean passwordGenerated = true;

        public User() {
        }

        public String getName() {
            return this.name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getPassword() {
            return this.password;
        }

        public void setPassword(String password) {
            if (StringUtils.hasLength(password)) {
                this.passwordGenerated = false;
                this.password = password;
            }
        }

        public List<String> getRoles() {
            return this.roles;
        }

        public void setRoles(List<String> roles) {
            this.roles = new ArrayList(roles);
        }

        public boolean isPasswordGenerated() {
            return this.passwordGenerated;
        }
    }

    public static class Filter {
        private int order = -100;
        private Set<DispatcherType> dispatcherTypes;

        public Filter() {
            this.dispatcherTypes = new HashSet(Arrays.asList(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST));
        }

        public int getOrder() {
            return this.order;
        }

        public void setOrder(int order) {
            this.order = order;
        }

        public Set<DispatcherType> getDispatcherTypes() {
            return this.dispatcherTypes;
        }

        public void setDispatcherTypes(Set<DispatcherType> dispatcherTypes) {
            this.dispatcherTypes = dispatcherTypes;
        }
    }
}


关闭验证功能

主配置类中排除安全框架的配置

//排除security的配置,不启用
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
public class main
{
    public static void main(String[] args) {
        SpringApplication.run(main.class,args);
    }
}

默认用户认证模块涉及到的三个类

UserDetailsService

当什么也没有配置的时候,账号和密码是由 Spring Security 定义生成的。而在实际项目中账号和密码都是从数据库中查询出来的。所以我们要通过自定义逻辑控制认证逻辑。如果需要自定义逻辑时,只需要实现 UserDetailsService 接口即可。接口定义如下:

public interface UserDetailsService {
    UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}

返回值: UserDetails

返回值 UserDetails 是一个接口,定义如下

public interface UserDetails extends Serializable {
//获取用户权限
    Collection<? extends GrantedAuthority> getAuthorities();

    String getPassword();

    String getUsername();
//账号是否未过期
    boolean isAccountNonExpired();
//账号是否未被锁定
    boolean isAccountNonLocked();
//凭证是否未过期,凭证就是密码
    boolean isCredentialsNonExpired();
//用户是否启用状态
    boolean isEnabled();
}

UserDetails的实现类之:User

要想返回 UserDetails的实例就只能返回接口的实现类。SpringSecurity 中提供了如下的实例。对于我们只需要使用里面的 User类即可。注意 User 的全限定路径是:

org.springframework.security.core.userdetails.User此处经常和系统中自己开发的 User 类弄混。

在 User 类中提供了很多方法和属性。

public class User implements UserDetails, CredentialsContainer {
    private static final long serialVersionUID = 550L;
    private static final Log logger = LogFactory.getLog(User.class);
    private String password;
    private final String username;
    private final Set<GrantedAuthority> authorities;
    private final boolean accountNonExpired;
    private final boolean accountNonLocked;
    private final boolean credentialsNonExpired;
    private final boolean enabled;
    .....

}

其中构造方法有两个,调用其中任何一个都可以实例化

UserDetails实现类 User类的实例。而三个参数的构造方法实际上也是调用 7 个参数的构造方法。

  • username:用户名

  • password:密码

  • authorities:用户具有的权限。此处不允许为 null

此处的用户名应该是客户端传递过来的用户名。而密码应该是从数据库中查询出来的密码。Spring Security 会根据 User 中的 password和客户端传递过来的 password进行比较。如果相同则表示认证通过,如果不相同表示认证失败。

authorities里面的权限对于后面学习授权是很有必要的,包含的所有内容为此用户具有的权限,如有里面没有包含某个权限,而在做某个事情时必须包含某个权限则会出现 403。通常都是通过AuthorityUtils.commaSeparatedStringToAuthorityList(“”) 来创建authorities 集合对象的。参数是一个字符串,多个权限使用逗号分隔。


方法参数

方法参数表示用户名。此值是客户端表单传递过来的数据。默认情况下必须叫 username,否则无法接收。

异常

UsernameNotFoundException用户名没有发现异常。在loadUserByUsername中是需要通过自己的逻辑从数据库中取值的。如果通过用户名没有查询到对应的数据,应该抛出UsernameNotFoundException,系统就知道用户名没有查询到。


PasswordEncoder

Spring Security 要求容器中必须有PasswordEncoder实例。所以当自定义登录逻辑时要求必须给容器注入PaswordEncoder的bean对象。

接口介绍

  • encode():把参数按照特定的解析规则进行解析。

  • matches() :验证从存储中获取的编码密码与编码后提交的原始密码是否匹配。如果密码匹配,则返回 true;如果不匹配,则返回 false。第一个参数表示需要被解析的密码。第二个参数表示存储的密码。

  • upgradeEncoding():如果解析的密码能够再次进行解析且达到更安全的结果则返回 true,否则返回 false。默认返回 false。

public interface PasswordEncoder {
//加密
    String encode(CharSequence var1);
//匹配
    boolean matches(CharSequence var1, String var2);
//二次加密
    default boolean upgradeEncoding(String encodedPassword) {
        return false;
    }
}

内置解析器介绍


BCryptPasswordEncoder 简介

BCryptPasswordEncoder 是 Spring Security 官方推荐的密码解析器,平时多使用这个解析器。

BCryptPasswordEncoder 是对 bcrypt强散列方法的具体实现。是基于Hash算法实现的单向加密。可以通过strength控制加密强度,默认 10.

代码演示

新建测试方法BCryptPasswordEncoder 用法。

@SpringBootTest(classes = main.class)
public class Test
{
  @org.junit.jupiter.api.Test
 public void test()
 {
     //创建解析器
     PasswordEncoder pw = new BCryptPasswordEncoder();
     //对密码加密
     String encode = pw.encode("123");
     System.out.println(encode);

     //判断原字符和加密后内容是否匹配
     boolean matches = pw.matches("123", encode);
     System.out.println("==================="+matches);
 }
}


自定义登录逻辑

当 进 行 自 定 义 登 录 逻 辑 时 需 要 用 到 之 前 讲 解 的UserDetailsServicePasswordEncoder。但是 Spring Security 要求:当进行自定义登录逻辑时容器内必须有 PasswordEncoder实例。所以不能直接 new 对象。

编写配置类

@Configuration
public class SecurityConfig {

   @Bean
   public PasswordEncoder getPw(){
      return new BCryptPasswordEncoder();
   }
}

自定义逻辑

在 Spring Security 中实现 UserDetailService 就表示为用户详情服务。在这个类中编写用户认证逻辑。

@Service
public class UserServiceImpl implements UserDetailsService {
   @Autowired
   private PasswordEncoder pw;

   @Override
   public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
      //1.查询数据库判断用户名是否存在,如果不存在抛出UsernameNotFoundException异常
      if (!"admin".equals(username)){
         throw new UsernameNotFoundException("用户名不存在");
      }
      //2.把查询出来的密码(注册时已经加密过)进行解析,或直接把密码放入构造方法中
      String password = pw.encode("123");
      return new User(username,password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal"));
   }
}

其框架会把提交的密码使用我们定义的passwordEncode加密后调用**org.springframework.security.crypto.password.PasswordEncoder#matches**方法,与 返回的User中的密码进行比对。配对正常就验证通过。

查看效果

重启项目后,在浏览器中输入账号:admin,密码:123。后可以正确进入到 login.html 页面。


自定义登录页面

虽然 Spring Security 给我们提供了登录页面,但是对于实际项目中,大多喜欢使用自己的登录页面。所以 Spring Security 中不仅仅提供了登录页面,还支持用户自定义登录页面。实现过程也比较简单,只需要修改配置类即可。

编写登录页面

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/login" method="post">
    用户名:<input type="text" name="username" /><br/>
    密码:<input type="password" name="password" /><br/>
    <input type="submit" value="登录" />
</form>
</body>
</html>

修改配置类

修改配置类中主要是设置哪个页面是登录页面。配置类需要继承WebSecurityConfigurerAdapter,并重写 configure 方法。

  • successForwardUrl():登录成功后跳转地址

  • loginPage() :登录页面

  • loginProcessingUrl:登录页面表单提交地址,此地址可以不真实存在。

  • antMatchers():匹配内容

  • permitAll():允许

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

   @Override
   protected void configure(HttpSecurity http) throws Exception {
      //表单提交
      http.formLogin()
            //自定义登录页面
            .loginPage("/login.html")
            //当发现/login时认为是登录,必须和表单提交的地址一样。去执行UserServiceImpl
            .loginProcessingUrl("/login"

以上是关于Spirng Security知识点整理的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security----RBAC权限控制模型,和权限相关知识点整理

spirng注解配置核心示例

JSP页面开发知识点整理

IOS开发-OC学习-常用功能代码片段整理

VS2015 代码片段整理

小程序各种功能代码片段整理---持续更新