之前有讨论过security空间基于内存修改密码的,因为绝大部分应用的用户及其用户信息都是存到数据库当中的,所以,修改密码肯定也是基于数据库操作的。
其实基于内存的修改密码和基于数据库的修改密码原理基本上是一样的,只不过UserDetailService的实现类已经ChangePassword()方法的实现方式不同而已。在这里重新提一遍修改密码的原理(基于数据库):
我们知道基于内存的用户数据是存到内存的一个Map中的,但是无论存到内存中还是存到数据库中,当用户登录的时候都是调用一个UserDetailService接口的方法loadUserByUsername来把User查出来,然后放到SecurityContextHolder(security框架的用户凭证的维护中心)来维护的。
我们知道它的原理之后,就很容易发现,基于内存和基于数据库的不同就是UserDetailService接口的实现类不同,loadUserByUsername的实现方法不同,所以我们实现密码修改时changePassword的实现方法也会不同,因为security的这两种情况只是中间那部分不同而已,所以我们关注的是UserDetailService接口的实现。上一节一节比对了基于内存认证和基于数据库认证的异同。这里就直接比较修改密码的异同。
首先,我们需要了解基于数据库密码修改的步骤:
1、用户数据存到数据库当中,肯定用修改数据库中的用户密码。
2、因为用户第一次登录使用的是旧密码,所以security框架中拥有的当前用户的凭证还是旧的,所以我们修改了密码之后要更新security框架中的用户凭证,而这个用户凭证是交给security框架中的securityContextHolder来维护的。所以我们修改了密码之后,只要创建一个新的用户凭证更新到色粗ContextHolder中,那么基于数据库修改密码的操作就真正的完成了。
因为这里提到securityContextHolder这个类,所以在这里有必要说明一下这个类:
SecurityContextHolder是security框架中的提供的类,它是里面维护了security的上下文信息,已经登录用户的权限,用户凭证等等信息。ps:为什么当前登录用户信息已经那么多用户同时登录时,securityContextHolder不仅不会丢失这些信息而且能把每一个用户的信息分得很清楚,是因为securityContextHolder是维护在线程中的,每个用户登录之后拥有的行程都是不同的,拥有的session也是不同的,所以securityContextHolder能准确无误的管理好各个用户的用户信息与用户凭证。
下面我们要实现基于用户修改密码,第一步,继承UserDetailService接口,声明我们需要的方法:
package service; import java.util.HashSet; import java.util.Set; import javax.annotation.Resource; import model.Users; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserCache; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.cache.NullUserCache; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import dao.BaseCollectionDao; import dao.BaseEntityDao; public interface HibernateUserDetailsService extends UserDetailsService { public void changePassword(String username,String newpassword,String oldpassword); }
第二步,实现上面定义的接口:
package service.impl; import java.util.HashSet; import java.util.Set; import javax.annotation.Resource; import model.Users; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserCache; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.core.userdetails.cache.NullUserCache; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import service.HibernateUserDetailsService; import dao.BaseCollectionDao; import dao.BaseEntityDao; @Service("hibernateUserDetailsService") public class HibernateUserDetailsServiceImpl implements HibernateUserDetailsService { private BaseCollectionDao baseCollectionDao; private BaseEntityDao baseEntityDao; private UserCache userCache = new NullUserCache(); @Resource(name="baseCollectionDao") public void setBaseCollectionDao(BaseCollectionDao baseCollectionDao) { this.baseCollectionDao = baseCollectionDao; } @Resource(name="baseEntityDao") public void setBaseEntityDao(BaseEntityDao baseEntityDao) { this.baseEntityDao = baseEntityDao; } @Transactional public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Users users = baseCollectionDao.findUniqueByProperty(Users.class, "username", username); if(users == null) throw new UsernameNotFoundException("用户" + username + " 不存在!!!"); String[] roles = users.getRoles().split(","); Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>(); for(String role : roles) { authorities.add(new SimpleGrantedAuthority(role)); } return new User(users.getUsername(), users.getPassword(), authorities); } @Transactional public void changePassword(String username,String newpassword,String oldpassword) { Authentication currentuser=SecurityContextHolder.getContext().getAuthentication(); if(currentuser==null) { // This would indicate bad coding somewhere throw new AccessDeniedException("Can't change password as no Authentication object found in context " + "for current user."); } Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); String username2=((UserDetails)principal).getUsername(); Users users=baseCollectionDao.findUniqueByProperty(Users.class, "username", username2); if(users.getPassword().equals(oldpassword)) { users.setPassword(newpassword); baseEntityDao.update(users); SecurityContextHolder.getContext().setAuthentication(createNewAuthentication(currentuser, newpassword)); userCache.removeUserFromCache(username); } } public Authentication createNewAuthentication(Authentication currentAuth, String newPassword) { UserDetails user = loadUserByUsername(currentAuth.getName()); UsernamePasswordAuthenticationToken newAuthentication = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities()); newAuthentication.setDetails(currentAuth.getDetails()); return newAuthentication; } }
由上面的代码可以看到,当我们完成了第一步把新密码更新到数据库之后,我们需要创建一个新的凭证Authentication,并且更新到SecurityContextHolder,因为security框架在匹配当前用户的信息以及用户权限,都是从SecurityContextHolder里面拿到这个Authentication凭证,然后再拿到相应的信息。创建新凭证的过程很简单,相信大家很容易看懂。最后一步是移除用户缓存里面的缓存的旧数据。
做好上面那几步,security基于数据库的密码修改功能就真正完成了。
相关推荐
Spring Security 基于数据库的权限管理配置
Spring Security 把授权信息写入数据库
用STS(Spring Tool Suite)开发的,spring mvc + spring security 实现的最简单的登录系统,无数据库。
主题: Spring Security —— 演讲者 张明星 1、Traditional Web App Security Dev 2、Spring Security 2.x Quick Start 3、Spring Security 2.x Overview 4、Dive Into Spring Security Authentication ...
基于Springboot和Spring security的校园疫情防控系统源码+数据库.zip基于Springboot和Spring security的校园疫情防控系统源码+数据库.zip基于Springboot和Spring security的校园疫情防控系统源码+数据库.zip基于...
基于springboot+SpringSecurity的一款权限认证系统源码+数据库.zip基于springboot+SpringSecurity的一款权限认证系统源码+数据库.zip基于springboot+SpringSecurity的一款权限认证系统源码+数据库.zip基于springboot...
在开源的SpringSide上作权限控制的demo,附带数据库。分层清晰,设计合理,系统松散耦合。结合业界流行的安全框架精心设计开发。
SpringBoot+SpringSecurity+JWT+MybatisPlus实现基于注解的权限验证,可根据注解的格式不同,做到角色权限控制,角色加资源权限控制等,粒度比较细化。 @PreAuthorize("hasAnyRole('ADMIN','USER')"):具有admin或...
SpringSecurity4 样例工程(自定义登录页,从数据库获取用户名密码)
Spring Security三份资料,实战Spring Security 3.x.pdf;Spring Security 3.pdf;Spring Security使用手册.pdf
NULL 博文链接:https://clongjava.iteye.com/blog/1453648
SpringBoot+SpringSecurity+Vue后台管理系统的开发源代码(前端+后端+数据库)SpringBoot+SpringSecurity+Vue后台管理系统的开发源代码(前端+后端+数据库)SpringBoot+SpringSecurity+Vue后台管理系统的开发源代码...
spring security spring security 中文文档
Spring Security in Action
spring security 基于oauth 2.0 实现 sso 单点登录Demo 使用 spring security 基于oauth 2.0 实现 sso 单点登录Demo spring boot + spring security + spring security oauth
Spring security认证授权例子,自动创建数据库,在SysUser类增加字段,即可动态增加数据库对应表sys_user字段(前提是要删除原表,启动应用时才会重建表)
springsecurity是一个功能强大且高度可定制的身份验证和访问控制框架。springsecurity是一个专注于为Java应用程序提供身份验证和授权的框架。与所有Spring项目一样,Spring安全性的真正威力在于它可以很容易地扩展以...
yshop基于当前流行技术组合的前后端分离商城系统: SpringBoot2+MybatisPlus+SpringSecurity+jwt+redis+Vue的前后端分离的商城系统, 包含分类、sku、运费模板、素材库、小程序直播、拼团、砍价、商户管理、 秒杀、...
java毕业设计——基于spring boot的音乐播放网站设计与实现(源码+数据库).zip java毕业设计——基于spring boot的音乐播放网站设计与实现(源码+数据库).zip java毕业设计——基于spring boot的音乐播放网站设计与...
SpringSecurity框架的权限认证流程原理,请求到来时SpringSecurity如果调用层层过滤器来完成认证;