本篇内容主要讲解“如何理解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;
@Service
public 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才能把从客户端接收到的密码使用同样的方式加密,然后做认证。
当然,你也可以通过下面的方式来设置密码加密器:
@Override
protected 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
一个比较抽象的概念,用于唯一标识实体的类。
例如,对于用户来说:用户id、手机号、邮箱就可以作为用户的principal来标志用户。
简单点理解,就可以看做是密码。
先知道是:角色与权限,具体的后面详细介绍。
用户实体的接口,通过这个接口可以获取:
getPassword: 获取密码
getUserName: 获取用户名
isEnabled: 账户是否可用
isAccountNonExpired: 账户是否过期
isAccountNonLocked: 账户是否被锁定
isCredentialsNonExpired: 密码是否过期
getAuthorites:获取用户权限,本质上是用户的角色信息
Spring Security中是:org.springframework.security.core.userdetails.UserDetails
Spring Security提供了一个默认的实现类:org.springframework.security.core.userdetails.User
这个接口只有一个目的,就是获取UserDetails,例如,我们需要从数据库获取用户,就需要实现这个接口,并把实现类注入到Spring容器中。
只需要获取UserDetails,认证的工作Spring Security自己获通过用户名和密码来验证,中间可能还涉及一下密码处理的过程。
用于存储用户认证详细信息: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<? extends GrantedAuthority> authorities) {
super(authorities);
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getPrincipal() {
return null;
}
}
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为什么能生效,是因为做了下面的配置:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailService);
}
认证管理器构建者AuthenticationManagerBuilder的userDetailsService会创建一个DaoAuthenticationProvider,DaoAuthenticationProvider使用DelegatingPasswordEncoder的是一个代理,默认使用的是BCryptPasswordEncoder。
AuthenticationManager,顾名思义,是用来管理Authentication,其实是管理AuthenticationProvider。
ProviderManager是AuthenticationManager的实现类,它持有一个AuthenticationProvider列表,用来完成不同的认证。
它还持有一个AuthenticationManager的引用,作为其父类。
一般来说我们使用ProviderManager就够了,一般也只需要一个AuthenticationProvider。
如果要配置多个,可以在SecurityConfig(继承了WebSecurityConfigurerAdapter)重写authenticationManager。
@Override
protected AuthenticationManager authenticationManager() {
// ProviderManager可以设置多个AuthenticationProvider
ProviderManager authenticationManager = new ProviderManager(Arrays.asList());
return authenticationManager;
}
简单的说就是用于对密码进行加密,存储用户密码的明文绝对不是什么好的习惯,这是对用户的不负责任,也是项目存在风险。
所以,我们一般都会对用户的密码进行加密之后再存储,通常的做法是加盐然后使用sha256再md5之类的算法。
使用Spring Security就需要我们自己实现这些过程了,直接配置PasswordEncoder就可以了。
Spring Security为PasswordEncoder提供了下面一些实现类。
即委托密码编码器,兼容多种不同加密方式存储密码。主要用于新旧数据的加密方式的兼容,做到平滑迁移,例如旧数据使用NoOpPasswordEncoder,新数据使用BCryptPasswordEncoder加密。
基于bcrypt算法的编码器,为了使其更能抵御密码破解,bcrypt故意降低了速度,与其他自适应单向功能一样,应将其调整为大约1秒钟,以验证系统上的密码。BCryptPasswordEncoder的默认实现使用强度10。
基于 Argon2算法的编码器,Argon2是一种故意慢速的算法,需要大量内存。 与其他自适应单向功能一样,应将其调整为大约1秒钟,以验证系统上的密码。 Argon2PasswordEncoder的当前实现需要BouncyCastle。
基于 PBKDF2算法的编码器,为了克服密码破解问题,PBKDF2是一种故意缓慢的算法。 与其他自适应单向功能一样,应将其调整为大约1秒钟,以验证系统上的密码。 当需要FIPS认证时,此算法是一个不错的选择。
基于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方法,我们可以看到上面的方式等价于:
@Bean
public 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原理认证”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/u/2474629/blog/5041907