1、通过shiro进行带盐加密,需要User表中有一个salt字段;
①md5加密:
用户密码是明文,这样是有巨大风险的。
所以,通常都会采用非对称加密,什么是非对称呢?就是不可逆的,而 md5 就是这样一个算法.
如代码所示 123 用 md5 加密后,得到字符串: 202CB962AC59075B964B07152D234B70
这个字符串,却无法通过计算,反过来得到源密码是 123.
这个加密后的字符串就存在数据库里了,下次用户再登陆,输入密码 123, 同样用md5 加密后,再和这个字符串一比较,就知道密码是否正确了。如此这样,既能保证用户密码校验的功能,又能保证不暴露密码。
②盐:
上面讲了md5加密,但是md5加密又有一些缺陷:
- 如果我的密码是 123,你的也是 123, 那么md5的值是一样的,那么通过比较加密后的字符串,我就可以反推过来,原来你的密码也是123.
- 与上述相同,虽然 md5 不可逆,但是我可以穷举法呀,我把特别常用的100万或者更多个密码的 md5 值记录下来,比如12345的,abcde的。 相当一部分人用的密码也是这些,那么只要到数据库里一找,也很快就可以知道原密码是多少了。这样看上去也就破解了,至少一部分没有想象中那么安全吧。
为了解决这个问题,引入了盐的概念。 盐是什么意思呢? 比如炒菜,直接使用md5,就是对食材(源密码)进行炒菜,因为食材是一样的,所以炒出来的味道都一样,可是如果加了不同分量的盐,那么即便食材一样,炒出来的味道也就不一样了。
所以,虽然每次 123 md5 之后都是202CB962AC59075B964B07152D234B70,但是 我加上盐,即 123+随机数,那么md5值不就不一样了吗? 这个随机数,就是盐,而这个随机数也会在数据库里保存下来,每个不同的用户,随机数也是不一样的。再就是加密次数,加密一次是202CB962AC59075B964B07152D234B70,我可以加密两次呀,就是另一个数了。 而黑客即便是拿到了加密后的密码,如果不知道到底加密了多少次,也是很难办的。
2、提供通过JPA进行验证的Realm,继承AuthorizingRealm:
public class JPARealm extends AuthorizingRealm
JPARealm 中有doGetAuthenticationInfo()方法,验证信息:
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String userName = token.getPrincipal().toString();
User user = userService.getByName(userName);
String passwordInDB = user.getPassword();
String salt = user.getSalt();
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName, passwordInDB, ByteSource.Util.bytes(salt),
getName());
return authenticationInfo;
}
用户登录时,在登录方法中:
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(name,userParam.getPassword());
subject.login(token);
放到token中,doGetAuthenticationInfo方法获取token,创建SimpleAuthenticationInfo对象,让输入密码加上盐进行加密与数据库中的密码进行比对;
3、shiro配置:
- getJPARealm() 指定了 Realm 使用 JPARealm。
- hashedCredentialsMatcher() 指定了 加密算法使用 md5,并且混进行2次加密。
@Configuration
public class ShiroConfiguration {
@Bean
public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(getJPARealm());
return securityManager;
}
@Bean
public JPARealm getJPARealm(){
JPARealm myShiroRealm = new JPARealm();
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm;
}
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");
hashedCredentialsMatcher.setHashIterations(2);
return hashedCredentialsMatcher;
}
/**
* 开启shiro aop注解支持.
* 使用代理方式;所以需要开启代码支持;
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
4、subject代表当前用户状态,在登录拦截器判断是否登录时:
@GetMapping("/forelogout")
public String logout( ) {
Subject subject = SecurityUtils.getSubject();
if(subject.isAuthenticated())
subject.logout();
return "redirect:home";
}
如果已登录,subject会是已验证状态,若未登录,没有已验证状态;
学习过程中的一些理解,做点笔记以便回顾,若有理解不当的地方,欢迎指教~