shiro在springboot中的使用
1,配置shiro
@Configuration
public class ShiroConfig {
/*
1,创建过滤器,加入自定义的安全管理器
2,loginurl在未登入的用户访问需要登入才能访问的网页时自动跳转,unauthorized同理,successurl用于成功访问后自动跳转的页面
(失效的原因为successurl会根据session中存储的信息进行跳转,如果请求被拦截session只会记住被拦截前的url进行跳转
解决方案:重写FormAuthenticationFilter中的issueSuccessRedirect方法将successurl固定)
3,权限控制通过map对路径控制
*/
@Bean
public ShiroFilterFactoryBean shirFilter(@Qualifier("securityManager")SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> filterChainDefinitionMap = new HashMap<String, String>();
shiroFilterFactoryBean.setLoginUrl("/admin/login");//登入页面
shiroFilterFactoryBean.setUnauthorizedUrl("/admin/unauthc");//没授权的页面
shiroFilterFactoryBean.setSuccessUrl("/admin/successlogin");//登入成功的页面
filterChainDefinitionMap.put("/*", "anon");//所有用户都能访问
filterChainDefinitionMap.put("/admin/adminhome", "roles[admin]");//所有登录用户都能访问
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
//自定义的加密方式
@Bean
public PasswordHelper passwordHelper() {
return new PasswordHelper();
}
//创建匹配器用于转化用户输入的密码
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName(PasswordHelper.ALGORITHM_NAME); // 散列算法
hashedCredentialsMatcher.setHashIterations(PasswordHelper.HASH_ITERATIONS); // 散列次数
return hashedCredentialsMatcher;
}
//重写授权域并加入自定义的匹配器
@Bean(name="shiroRealm")
public CustomizeShiroRealm shiroRealm() {
CustomizeShiroRealm shiroRealm = new CustomizeShiroRealm();
shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); // 原来在这里
return shiroRealm;
}
//创建安全管理器,setrealm是必须操作的,在登入操作中ModularRealmAuthenticator类会对是否设置realm进行assert判断
@Bean(name = "securityManager")
public SecurityManager securityManager(@Qualifier("shiroRealm")CustomizeShiroRealm shiroRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroRealm);
return securityManager;
}
}
2,加密器
public class PasswordHelper {
private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
public static final String ALGORITHM_NAME = "md5"; // 基础散列算法
public static final int HASH_ITERATIONS = 2; // 自定义散列次数
/*
作用:在注册的时候为用户生成随机盐值以以及利用盐值进行加密
1,bytesource.util.bytes转换为的类似byte数组是为了后期比对密码的用的,
2,转换为hex是为了存储,因为在利用密码匹配器匹配密码时数据库中的密码都会转换为byte数组
*/
public void encryptPassword(User user) {
// 随机字符串作为salt因子
user.setSalt(randomNumberGenerator.nextBytes().toHex());
String newPassword = new SimpleHash(ALGORITHM_NAME, user.getPassword(),
ByteSource.Util.bytes(user.getCredentialsSalt()), HASH_ITERATIONS).toHex();
user.setPassword(newPassword);
}
}
3,重写授权域
public class CustomizeShiroRealm extends AuthorizingRealm {
@Autowired
private adminService adminservice;
/*
作用:获取授权信息,也就是该用户的角色和允许的操作
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
String username = (String) principals.getPrimaryPrincipal();
User user = adminservice.inserUserRolePerm(username);
authorizationInfo.addRole(user.getRole().getRole());
for (SysPermission permission : user.getRole().getPermissions()) {
authorizationInfo.addStringPermission(permission.getName());
}
return authorizationInfo;
}
/*
作用:获取用户数据库中的信息
1,SimpleAuthenticationInfo中password是对比用的,盐值是给输入的密码加密用的
2,getName的作用是获取当前域的名字,而这个名字是每个用户专属的name=类名+自增整数组成,作用:用来缓存标记,方法所属类CachingRealm
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
User user = adminservice.inserUserRolePerm(username);
if (user == null) {
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),
ByteSource.Util.bytes(user.getCredentialsSalt()), getName());
return authenticationInfo;
}
}
4,验证信息
问题一:subject代表什么,怎么生成的
subject从表面上来讲代表着用户,subject生成过程
- 将全局唯一securityManager注入到subjectcontext中
- 利用subjectcontext为参数执行securityManager中的createsubject(该函数目的为了确认安全管理器的注入,解析session(用来存权限),解析Principals(代表登入时存储的对象))
- 最后通过调用SubjectFactory构建DelegatingSubject对象
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
} catch (IncorrectCredentialsException ice) {
return new adminUnionDto<String>(responsestate.FAILLOGIN.state,responsestate.FAILLOGIN.msg,null);
} catch (UnknownAccountException uae) {
return new adminUnionDto<String>(responsestate.FAILLOGIN.state,responsestate.FAILLOGIN.msg,null);
}
User user = adminservice.inserUserRolePerm(username);
subject.getSession().setAttribute("user", user);
shiro验证的过程
-
验证是否设置realm,根据单realm或多realm选择对于处理方式,调用获取用户信息的函数,在获取用户信息的顺便认证
if (info != null) { this.assertCredentialsMatch(token, info); } else { log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}]. Returning null.", token); }
-
开始认证,利用上方设置的CredentialsMatcher来匹配代码为
//主要作用为:用数据库中的salt加密token中的密码然后用simplehash包装 Object tokenHashedCredentials = this.hashProvidedCredentials(token, info); //主要作用为:拿到数据库中密码并且转换为bytes然后用SimpleHash包装起来 Object accountCredentials = this.getCredentials(info); return this.equals(tokenHashedCredentials, accountCredentials);
- 给subject加上是否认证等标签包装好返回
shiro拓展
1,shiro的认证时限
默认过期单位为毫秒
subject.getSession().setTimeout(-1000L)//永不过期
subject.getSession().setTimeout(1800000)//默认的时间
2,缓存拓展
只要在securityManager设置cachemanager和sessionmanager就能使用,具体操作采用shiro-redis开源包,该包的重点为两个,(1)重写了序列化和反序列化方法,(2)可以自定义类加载器
@Bean
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
@Bean
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost("127.0.0.1:6379");
redisManager.setTimeout(0);
// redisManager.setPassword(password);
return redisManager;
}
@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
return sessionManager;
}
@Bean
public RedisSessionDAO redisSessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}
网友评论