当前位置: 首页 > news >正文

天津网站搜索引擎优化wordpress主题开发 书

天津网站搜索引擎优化,wordpress主题开发 书,佛山网站优化平台,html5网站制作实战前言 《Spring Security学习(五)——账号密码的存取》一文已经能满足一般应用的情况。但实际商业应用也会存在如下的情况:用户提交的账号密码,能在本地的保存的账号密码匹配上,或者能在远端服务认证中匹配上&#xff…

前言

《Spring Security学习(五)——账号密码的存取》一文已经能满足一般应用的情况。但实际商业应用也会存在如下的情况:用户提交的账号密码,能在本地的保存的账号密码匹配上,或者能在远端服务认证中匹配上,就算认证通过。这种通常类似于单点登录,或者认证中心之类的。本文不去探索这种架构,只是单纯讨论如果存在两种认证规则下如何处理。

多种认证方式使用Spring Security时有好几种处理方式。根据不同的情况,架构师考虑不同的解决方案。本文先从比较简单的方案说起。

假设能匹配数据库保存的账号密码,或者能匹配内存中保存的账号密码两者之一,就算认证通过。附加条件:

  1. 这两种认证方式是通过同一方式提交用户密码;
  2. 这两种认证方式是一条链式的处理方式。比如先判断第一种方式是否通过认证,不通过则判断第二种。

上述情况,我们考虑使用多个provider去处理。

架构

前面的文章讲过ProviderManager是AuthenticationManager的实现。其架构图如下:

在《Spring Security学习(五)——账号密码的存取》 中,我们说过DaoAuthenticationProvider撬动UserDetailsService和PasswordEncoder的“杠杆”。DaoAuthenticationProvider则是AuthenticationProvider的默认实现。Spring Security通过AuthenticationProvider调用获取UserDetails,然后进行匹配,最后返回UsernamePasswordAuthenticationToken(Authentication接口的具体)。

对于Spring Security的当存在多个AuthenticationProvider的实现时,用户提交的账号密码满足其中任意一个AuthenticationProvider,返回正确的Authentication即可。

自定义AuthenticationProvider

我们做个简单的自定义AuthenticationProvider。自定义MyProvider类:

@Data
public class MyProvider extends AbstractUserDetailsAuthenticationProvider{private UserDetailsServiceImpl userDetailsServiceImpl;private PasswordEncoder passwordEncoder;private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";private volatile String userNotFoundEncodedPassword;@Overrideprotected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)throws AuthenticationException {prepareTimingAttackProtection();try {UserDetails loadedUser = userDetailsServiceImpl.loadUserByMemory(username);if (loadedUser == null) {throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");}return loadedUser;}catch (UsernameNotFoundException ex) {mitigateAgainstTimingAttack(authentication);throw ex;}catch (InternalAuthenticationServiceException ex) {throw ex;}catch (Exception ex) {throw new InternalAuthenticationServiceException(ex.getMessage(), ex);}}@Overrideprotected void additionalAuthenticationChecks(UserDetails userDetails,UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {if (authentication.getCredentials() == null) {this.logger.debug("Failed to authenticate since no credentials provided");throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));}String presentedPassword = authentication.getCredentials().toString();if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {this.logger.debug("Failed to authenticate since password does not match stored value");throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));}}private void prepareTimingAttackProtection() {if (this.userNotFoundEncodedPassword == null) {this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD);}}private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) {if (authentication.getCredentials() != null) {String presentedPassword = authentication.getCredentials().toString();this.passwordEncoder.matches(presentedPassword, this.userNotFoundEncodedPassword);}}
}

自定义Provider应该实现AuthenticationProvider接口,并实现authenticate方法。这里我们参考DaoAuthenticationProvider来写,重写retrieveUser方法调用我们自定义的userDetailsServiceImpl的loadUserByMemory方法从内存获取。additionalAuthenticationChecks方法的作用是对提交的密码和系统中保存的密码进行匹配,这里直接参考DaoAuthenticationProvider类的写法。prepareTimingAttackProtection和mitigateAgainstTimingAttack都是用来防御计时攻击的,非认证必要。

在《Spring Security学习(二)——使用数据库保存密码》中我们定义了SysUserServiceImpl,这里进行一些修改:

@Component
public class UserDetailsServiceImpl implements UserDetailsService{@Autowiredprivate SysUserService userService;private static Map<String, SysUserEntity> userMap;static {userMap = new HashMap<String, SysUserEntity>();SysUserEntity sysUser = new SysUserEntity();sysUser.setUsername("test");sysUser.setPassword("test###");userMap.put("test", sysUser);}@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {QueryWrapper<SysUserEntity> queryWrapper = new QueryWrapper<SysUserEntity>();queryWrapper.eq("username", username);queryWrapper.last("limit 1");SysUserEntity user = userService.getOne(queryWrapper);if(user == null) {throw new UsernameNotFoundException("username not found");}return (new LoginUser(user));}public UserDetails loadUserByMemory(String username) throws UsernameNotFoundException {if(StrUtil.isNotEmpty(username)) {SysUserEntity user = userMap.get(username);if(user == null) {throw new UsernameNotFoundException("username not found");}return (new LoginUser(user));}return null;}
}

首先我们增加一个loadUserByUsername方法,用于读取map中保存的测试账号。另外之前当我们查询找不到对象的时候,抛出了RuntimeException,按照Spring Security的设计思想来说是不对的,应该抛出UsernameNotFoundException让上层接住这个异常。抛出RuntimeException会直接终止后面provider的匹配,不符合Spring Security的设计理念。

自定义密码加密器还是使用之前文章的版本MyPasswordEncoder,为原密码后加3个#:

public class MyPasswordEncoder implements PasswordEncoder {@Overridepublic String encode(CharSequence rawPassword) {return rawPassword + "###";}@Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) {if (encodedPassword.equals(rawPassword + "###")) {return true;}return false;}
}

另外由于自定义加密方式,所以数据库密码也要改一下:

最后修改一下WebSecurityConfig的配置,把自定义的MyProvider加进去:

@EnableWebSecurity
public class WebSecurityConfig{@Beanpublic MyPasswordEncoder PasswordEncoder() {return new MyPasswordEncoder();}@Beanpublic SecurityFilterChain formLoginFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated()).formLogin(Customizer.withDefaults());MyProvider myProvider = new MyProvider();myProvider.setPasswordEncoder(PasswordEncoder());UserDetailsServiceImpl UserDetailsServiceImpl = SpringUtils.getBean(UserDetailsServiceImpl.class);myProvider.setUserDetailsServiceImpl(UserDetailsServiceImpl);http.authenticationProvider(myProvider);DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();daoAuthenticationProvider.setPasswordEncoder(PasswordEncoder());daoAuthenticationProvider.setUserDetailsService(UserDetailsServiceImpl);http.authenticationProvider(daoAuthenticationProvider);return http.build();}
}

16行新建MyProvider,17行设置自定义的加密器,18-19行设置UserDetailsServiceImpl。20行加到HttpSecurity中。22行设置DaoAuthenticationProvider到HttpSecurity中。

之后我们同样是访问/hello路径,然后在默认登录页分别尝试使用jake/123,test/test两个账号登陆。会发现通过两种认证方式都可以登陆成功。

小结

本文使用自定义Provider的方式让用户匹配多种认证方式。这个只是最简单的方式。从系列文章的本文章开始,就涉及很多需要了解Spring Security架构设计的情况,读者最好还是阅读源码去理解。否则生搬硬套很难达到实际开发想要达到的目的。本文主要涉及ProviderManager、DaoAuthenticationProvider、AbstractUserDetailsAuthenticationProvider的源码。读者最好阅读一下以了解本文实现的思路。

其实还有个小疑问,WebSecurityConfig似乎我们去掉DaoAuthenticationProvider的配置,一样能达到同样的效果,那我们的配置是不是有点多余了?其实这涉及另一个概念,留到下一篇文章再讲。

http://www.yayakq.cn/news/666048/

相关文章:

  • 万网网站建设方案书新开传奇网站999新服网
  • wordpress企业站模板网站备案管理
  • 携程旅游网官方网站 做攻略wordpress 访问
  • 广东做网站找谁工业产品设计图
  • 建设网站书籍手机电脑网站设计
  • 做汽车行业必须注册际零件网站wordpress 评论提醒
  • 贵阳网站建设哪家便宜国外购物网站有哪些
  • 做网站开发的笔记本配置网站建设公司客户开发手册
  • 初学者自己做网站北京小程序开发电话
  • 大连flash网站gateface做网站
  • 怎么做网站讯息免费申请流量卡
  • 有没有什么专门做兼职的网站吗手机网站模板 餐饮
  • 金华建设工程网站国际国内时事新闻
  • 瑞安网站制作建设社区服务网站的论文
  • 局域网里做网站域名注册了是永久的吗
  • 网站成品免费下载推广服务公司
  • 鹤壁做网站优化网站类型定义
  • 网站建设步骤视频教程扩展名 网站
  • 美橙互联网站打不开网页版微信文件传输助手
  • ps做网站界面wordpress 中间截取缩略图
  • 手机怎么制作网站教程做网站排名费用
  • 织梦 更换网站图标阿里云服务器上传网站
  • 南通网站建设知识vivo系统最新版本
  • 手机产品展示网站模板建站网站那个好
  • steam做皮肤网站如何把wordpress的文章页写成模板
  • wordpress 做外贸站教育机构做网站的目的
  • 北京品牌网站建设海口建站费用
  • 一对一直播网站开发做网站用什么配资电脑
  • 淄博网站设计丨致信网络建设能播放视频的网站
  • 口碑好网站建设多少钱网站推广 优帮云