十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
本篇内容主要讲解“如何理解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原理认证”有了更深的了解,不妨来实际操作一番吧!这里是创新互联网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!