千家信息网

如何理解SpringSecurity原理认证

发表于:2025-02-01 作者:千家信息网编辑
千家信息网最后更新 2025年02月01日,本篇内容主要讲解"如何理解SpringSecurity原理认证",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"如何理解SpringSecurity原理认证"
千家信息网最后更新 2025年02月01日如何理解SpringSecurity原理认证

本篇内容主要讲解"如何理解SpringSecurity原理认证",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"如何理解SpringSecurity原理认证"吧!

项目结构基本没有变:

首先我们需要实现UserDetailsService,来获取用户相关的信息封装类UserDetails。

import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.core.userdetails.User;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.stereotype.Service;@Servicepublic class MyUserDetailService implements UserDetailsService {        @Override        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//              SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("admin");        // 这里可以访问数据库、缓存等获取用户信息,然后构建为User,User是UserDetails的实现类                return User.builder()                                .username("bob")//用户名                //密码 111111 通过BCryptPasswordEncoder加密之后的密文                                .password("$2a$10$344aKAgXr17q7u.8l5i7Cu8wUJr/cxBIniLsVtf/WwFrPx0khY62K")                                .authorities("admin")//权限信息                                .build();        }}

UserDetailsService只做一件事情,就是获取UserDetails,主要是用户名、密码和权限。可以使用Spring Security为我们提供的实现类User,也可以自己实现UserDetails,按自己需求封装。

有了怎样获取UserDetails的方法,我们当然还要通过SecurityConfig配置的AuthenticationManagerBuilder告诉Spring Security。

import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import vip.oschool.uinion.security.MyUserDetailService;import javax.annotation.Resource;@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true)public class SecurityConfig extends WebSecurityConfigurerAdapter {        @Resource        MyUserDetailService myUserDetailService;        @Bean        BCryptPasswordEncoder bCryptPasswordEncoder() {                return new BCryptPasswordEncoder();        }        @Override        protected void configure(AuthenticationManagerBuilder auth) throws Exception {                auth.userDetailsService(myUserDetailService);        }}

注意,我们同时还添加了一个BCryptPasswordEncoder,这是一个密码加密器,因为一般情况数据库存放的都是密码的密文,所以,我们还要告诉Spring Security,我们数据库的密码的加密方式是什么,这样Spring Security才能把从客户端接收到的密码使用同样的方式加密,然后做认证。

当然,你也可以通过下面的方式来设置密码加密器:

@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {    auth.userDetailsService(myUserDetailService).passwordEncoder(new BCryptPasswordEncoder());}

下面我们把配置文件中的用户名和密码注释掉,重新启动,然后就可以通过我们在UserDetailsService实现中设置的用户bob和密码111111来完成认证。

server:  port: 8081#spring:#    security:#      user:#        name: tim#        password: 111111

Principal

一个比较抽象的概念,用于唯一标识实体的类。

例如,对于用户来说:用户id、手机号、邮箱就可以作为用户的principal来标志用户。

Credentials

简单点理解,就可以看做是密码。

GrantedAuthority

先知道是:角色与权限,具体的后面详细介绍。

UserDetails

用户实体的接口,通过这个接口可以获取:

  • getPassword: 获取密码

  • getUserName: 获取用户名

  • isEnabled: 账户是否可用

  • isAccountNonExpired: 账户是否过期

  • isAccountNonLocked: 账户是否被锁定

  • isCredentialsNonExpired: 密码是否过期

  • getAuthorites:获取用户权限,本质上是用户的角色信息

Spring Security中是:org.springframework.security.core.userdetails.UserDetails

Spring Security提供了一个默认的实现类:org.springframework.security.core.userdetails.User

UserDetailsService

这个接口只有一个目的,就是获取UserDetails,例如,我们需要从数据库获取用户,就需要实现这个接口,并把实现类注入到Spring容器中。

只需要获取UserDetails,认证的工作Spring Security自己获通过用户名和密码来验证,中间可能还涉及一下密码处理的过程。

Authentication

用于存储用户认证详细信息:principal、权限GrantedAuthority等。

自定义的Authentication可以实现Authentication接口,也可以直接继承AbstractAuthenticationToken。

import org.springframework.security.authentication.AbstractAuthenticationToken;import org.springframework.security.core.GrantedAuthority;import java.util.Collection;public class JwtAuthenticationToken extends AbstractAuthenticationToken {    public JwtAuthenticationToken(Collection authorities) {        super(authorities);    }    @Override    public Object getCredentials() {        return null;    }    @Override    public Object getPrincipal() {        return null;    }}

AuthenticationProvider

AuthenticationProvider,顾名思义,认证的提供者,就是用于执行特定类型的身份认证的接口。

我们如果要自定义认证,就需要实现这个接口,例如,现在很多前后端分离项目使用JWT方式来鉴权,那每个请求怎样校验Token呢?

我们就可以创建一个JWTAuthenticationProvider实现AuthenticationProvider,来完成JWT token的认证工作。

import org.springframework.security.authentication.AuthenticationProvider;import org.springframework.security.core.Authentication;import org.springframework.security.core.AuthenticationException;public class JwtAuthenticationProvider implements AuthenticationProvider {    @Override    public Authentication authenticate(Authentication authentication) throws AuthenticationException {        return null;    }    @Override    public boolean supports(Class authentication) {        return authentication.isAssignableFrom(JwtAuthenticationToken.class);    }}

然后可以通过下面的方式把我们自定义的AuthenticationProvider注入。

@EnableWebSecurity()public class SecurityConfig extends WebSecurityConfigurerAdapter {    @Override    protected void configure(AuthenticationManagerBuilder auth) throws Exception {        auth.authenticationProvider(daoAuthenticationProvider())                .authenticationProvider(jwtAuthenticationProvider());    }}

前面,我们使用的UserDetailService为什么能生效,是因为做了下面的配置:

@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {    auth.userDetailsService(userDetailService);}

认证管理器构建者AuthenticationManagerBuilder的userDetailsService会创建一个DaoAuthenticationProvider,DaoAuthenticationProvider使用DelegatingPasswordEncoder的是一个代理,默认使用的是BCryptPasswordEncoder。

AuthenticationManager与ProviderManager

AuthenticationManager,顾名思义,是用来管理Authentication,其实是管理AuthenticationProvider。

ProviderManager是AuthenticationManager的实现类,它持有一个AuthenticationProvider列表,用来完成不同的认证。

它还持有一个AuthenticationManager的引用,作为其父类。

一般来说我们使用ProviderManager就够了,一般也只需要一个AuthenticationProvider。

如果要配置多个,可以在SecurityConfig(继承了WebSecurityConfigurerAdapter)重写authenticationManager。

 @Overrideprotected AuthenticationManager authenticationManager() {    // ProviderManager可以设置多个AuthenticationProvider    ProviderManager authenticationManager = new ProviderManager(Arrays.asList());    return authenticationManager;}

PasswordEncoder

简单的说就是用于对密码进行加密,存储用户密码的明文绝对不是什么好的习惯,这是对用户的不负责任,也是项目存在风险。

所以,我们一般都会对用户的密码进行加密之后再存储,通常的做法是加盐然后使用sha256再md5之类的算法。

使用Spring Security就需要我们自己实现这些过程了,直接配置PasswordEncoder就可以了。

Spring Security为PasswordEncoder提供了下面一些实现类。

DelegatingPasswordEncoder

即委托密码编码器,兼容多种不同加密方式存储密码。主要用于新旧数据的加密方式的兼容,做到平滑迁移,例如旧数据使用NoOpPasswordEncoder,新数据使用BCryptPasswordEncoder加密。

BCryptPasswordEncoder

基于bcrypt算法的编码器,为了使其更能抵御密码破解,bcrypt故意降低了速度,与其他自适应单向功能一样,应将其调整为大约1秒钟,以验证系统上的密码。BCryptPasswordEncoder的默认实现使用强度10。

Argon2PasswordEncoder

基于 Argon2算法的编码器,Argon2是一种故意慢速的算法,需要大量内存。 与其他自适应单向功能一样,应将其调整为大约1秒钟,以验证系统上的密码。 Argon2PasswordEncoder的当前实现需要BouncyCastle。

Pbkdf2PasswordEncoder

基于 PBKDF2算法的编码器,为了克服密码破解问题,PBKDF2是一种故意缓慢的算法。 与其他自适应单向功能一样,应将其调整为大约1秒钟,以验证系统上的密码。 当需要FIPS认证时,此算法是一个不错的选择。

SCryptPasswordEncoder

基于scrypt算法的编码器, 为了克服自定义硬件scrypt上的密码破解问题,这是一种故意缓慢的算法,需要大量内存。 与其他自适应单向功能一样,应将其调整为大约1秒钟,以验证系统上的密码。

添加测试用户

我们测试的时候,想要添加测试用户,但是又不想太麻烦的去实现UserDetailsService,也可以通过下面的方式:

@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true)public class SecurityConfig extends WebSecurityConfigurerAdapter {        @Override        protected void configure(AuthenticationManagerBuilder auth) throws Exception {                auth.inMemoryAuthentication()                                .withUser("tim")                                .password("111111").roles("admin")                                .and()                                .withUser("allen")                                .password("222222")                                .roles("user");        }}

进入inMemoryAuthentication方法,我们可以看到上面的方式等价于:

@Beanpublic UserDetailsService userDetailsService() {    InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();    manager.createUser(User.withUsername("tim").password("111111").roles("admin").build());    manager.createUser(User.withUsername("allen").password("222222").roles("user").build());    return manager;}

所以,本质上还是相当于创建了一个UserDetailsService,只不过数据保存在内存中。

到此,相信大家对"如何理解SpringSecurity原理认证"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

0