有个叫若依的后台管理框架挺流行的,我们也在用。经过一段时间发展,若依有了两个版本,前后端分离和未分离版本,前一个版本用的是spring security验证框架,后一个用的是shiro。
有时候我们需要跟若依共用数据库搭建springboot服务,之前用jwt+spring security搭建过,所以拿过来直接用,用的时候发现问题,登录总是失败无法获取token。定位半天才发现,这个老版本的若依用的是shiro。于是,问题来了,怎么在现有的工程中,改动最少,把spring security 的验证方法改成shiro的验证方法(MD5加盐)?
从AuthenticationManager.authenticate,这个入口开始,一步一步看源码,不过spring的代码代理来注入去,看着让人头大,很难把整个流程理清楚,所以也很难找到切入点。直到看到 DaoAuthenticationProvider 这个类的时候才豁然开朗。 密码验证是在这里做的,我只要照着DaoAuthenticationProvider写一个实现类替换它就行了。
import org.apache.shiro.crypto.hash.Md5Hash;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import com.xxxxxx.data.board.model.UserDetail;
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider{
private UserDetailsService userDetailsService;
public DaoAuthenticationProvider(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
String newPassword = authentication.getCredentials().toString();
if (!matches((UserDetail)userDetails, newPassword)) {
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
}
public boolean matches(UserDetail userDetail, String newPassword) {
return userDetail.getPassword().equals(encryptPassword(userDetail.getUsername(), newPassword, userDetail.getSalt()));
}
public String encryptPassword(String username, String password, String salt) {
return new Md5Hash(username + password + salt).toHex().toString();
}
@Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
UserDetails loadedUser = this.userDetailsService.loadUserByUsername(username);
if (loadedUser == null) {
throw new InternalAuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
}
然后要做的就是修改 WebSecurityConfig 将新的实现类注入
如此就把spring security 的 BCrypt方式加密 替换成了 shiro 的MD5加盐方式加密。