为了进一步加强用户体验,提供密码修改功能是必须的。因为密码修改功能更多的是应用在用户名和密码都存到数据库的,所以这里基于内存修改密码不会关注存储机制,更多的会关注security框架本身对这种方式的拓展的整体流程和设计。
security框架中提供了一个UserDetailService的实现类InMemoryDaoImpl的类来管理内存用户,它的内存凭证存储使用了一个Map来存储内存中的用户和所关联的UserDetails。而这个UserDetails的实现类,security已经封装好了——o.s.s.core.userDetails.User。
既然我们知道了是InMemoryDaoImpl来管理内存中的用户,那我们只需要对UserDetailService以及它的实现类InMemoryDaoImpl进行拓展就可以达到密码修改的功能了。
首先我们需要做的是拓展UserDetailService以及它的实现类InMemoryDaoImpl,拓展完成之后,我们就想办法把我们拓展后的类覆盖掉security框架中原有的处理类。
第一步,拓展UserDetailService和InMemoryDaoImpl:
UserDetailService拓展:我们需要一个修改密码的方法,而UserDetailService是不具有的,所以我们需要创建一个继承了UserDetailService的类IChangePassword:
package securityservice;
import org.springframework.security.core.userdetails.UserDetailsService;
//imports omitted
public interface IChangePassword extends UserDetailsService{
void changePassword(String username,String password);
}
InMemoryDaoImpl拓展:这里的拓展我们需要保留它之前的方法,而且要实现我们刚拓展的那个修改密码的方法,所以需要继承InMemoryDaoImpl和实现IChangePassword:
package securityDaoImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.memory.InMemoryDaoImpl;
import securityservice.IChangePassword;
public class InMemoryChangePasswordDaoImpl extends InMemoryDaoImpl implements IChangePassword {
public void changePassword(String username,String password)
{
//get the UsrDetails
User userDetails=(User)getUserMap().getUser(username);
//creat a new UserDetails with the new password
User newUserDetails=new User(userDetails.getUsername(),password,userDetails.isEnabled(),userDetails.isAccountNonExpired(),userDetails.isCredentialsNonExpired(),userDetails.isAccountNonLocked(),userDetails.getAuthorities());
//add to the map
getUserMap().addUser(newUserDetails);
}
}
我们可以看到上面的实现代码,它从存储集合Map里面拿到当前的那个UserDetails凭证,然后创建一个新的UserDetails,修改了密码,然后保存到Map里,覆盖掉之前那个。
这里的拓展代码并不难实现,最主要的是理解它的整个处理流程和设计。
现在已经拓展完了,下一步需要把我们拓展好的类放到security框架中,覆盖掉原有的那个,这需要从配置文件里下手。ps:(这里可能由于每个人的命名空间的不同,所以配置文件的标签会有不一样,不过这并不影响。)
第二步:配置拓展后的UserDetailService到security框架中:
security有提供配置自定义UserDetailService的方法:
<authentication-manager alias="authenticationManager"> <authentication-provider user-service-ref="InMemoryChangePasswordDaoImpl"> </authentication-provider>
如上面代码显示,user-service-ref这个属性就是为了方便开发工程中拓展UserDetailService所提供的。
现在已经将其配置到security里面了,下一步要在配置文件初始化这个bean:
<beans:bean id="InMemoryChangePasswordDaoImpl" class="securityDaoImpl.InMemoryChangePasswordDaoImpl"> <beans:property name="userProperties"> <beans:props> <beans:prop key="guest">guest,ROLE_USER</beans:prop> </beans:props> </beans:property> </beans:bean>
到这一步,基于内存修改密码的功能已经拓展完了,剩下的工作只需要在controller里面调用这个改密的方法就ok了,下面是笔者本人的controller代码(只供参考):
package controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import securityservice.IChangePassword;
@Controller
public class ChangePasswordController {
@Autowired
private IChangePassword iChangePasswordDao;
@RequestMapping(value="/changePassword",method=RequestMethod.POST)
public String changePassword(@RequestParam("newPassword") String newpassword,@RequestParam("oldPassword") String oldpassword)
{
Object principal =SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String username=principal.toString();
if(principal instanceof UserDetails)
{
username=((UserDetails)principal).getUsername();
}
// if(((UserDetails)principal).getPassword().equals(oldpassword))
// {
iChangePasswordDao.changePassword(username, newpassword);
// }
return "redirect:home.jsp";
}
}
到这里,整个后台过程已经拓展完了,其实这里的拓展并不难,主要还是要理解其整体的处理流程和设计方法,然后按照security框架作者的设计思想去拓展,然后就可以水到渠成了。
本文是笔者学习经验所得,仅供参考,希望能帮到有需要的人。