8.整合shiro
8.1shiroConfig
1. SecurityManager
安全管理器
1.可以说是所有配置的入口,简化配置,方便使用。
2.一个接口就可以实现,验证的操作(登录、退出)、授权(授权访问指定资源、角色)、Session管理。
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager defaultWebSecurityManager;
(defaultWebSecurityManager = new DefaultWebSecurityManager()).setRealm(this.realm());//将登录用户的认证和权限信息交给securityManager管理
defaultWebSecurityManager.setSessionManager(this.sessionManager());//将session交给securityManager管理
defaultWebSecurityManager.setCacheManager( this.redisCacheManager());//将缓存交给securityManager管理
return defaultWebSecurityManager;
}
2. Realm
主要用作封装认证、授权信息,返回给安全管理器处理
@Bean
public Realm realm() {
return new UserAuthorizingRealm();
}
3. ShiroFilter
拦截需要安全控制的url,然后进行相应的权限控制。
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
HashMap filters = new HashMap();
filters.put("rest", new RestShiroFilter());
shiroFilterFactoryBean.setFilters( filters);
shiroFilterFactoryBean.setSecurityManager(securityManager);
LinkedHashMap<String, String> filterChainDefinitionMap;
(filterChainDefinitionMap = new LinkedHashMap<>()).put("/user", "anon");//可以匿名访问
filterChainDefinitionMap.put("/captcha", "anon");//可以匿名访问
filterChainDefinitionMap.put("/captcha/verify", "anon");//可以匿名访问
filterChainDefinitionMap.put("/user/login", "anon");//可以匿名访问
filterChainDefinitionMap.put("/user/success", "anon");//可以匿名访问
filterChainDefinitionMap.put("/swagger-ui.html", "anon");//可以匿名访问
filterChainDefinitionMap.put("/swagger-resources/**", "anon");//可以匿名访问
filterChainDefinitionMap.put("/webjars/**", "anon");//可以匿名访问
filterChainDefinitionMap.put("/druid/**", "anon");//可以匿名访问
filterChainDefinitionMap.put("/error/**", "anon");//可以匿名访问
filterChainDefinitionMap.put("/**", "rest");//根据请求的方法
shiroFilterFactoryBean.setLoginUrl("/error/403"); //没有登录的用户请求到登陆页面的路径
shiroFilterFactoryBean.setSuccessUrl("/user/success");登录成功的跳转页面
shiroFilterFactoryBean.setUnauthorizedUrl("/error/403");无权限页面
shiroFilterFactoryBean.setFilterChainDefinitionMap( filterChainDefinitionMap);
return shiroFilterFactoryBean;
在ShiroFilterFactoryBean中配置是否认证的规则时,一定要注意使用的容器是否是有序的,shiro根据配置的规则进行拦截认证时,是根据容器中的存储顺序决定的。
项目中使用的是LinkedHashMap。
无权限页面跳转:这个过滤器必须要是AuthorizationFilter
过滤器才行,只有perms,roles,ssl,rest,port才是属于AuthorizationFilter,而anon,authcBasic,authc,user是AuthenticationFilter,所以unauthorizedUrl设置后页面不跳转。
使用注解注解开发:
//在具体的方法上添加注解
@RequiresRoles(value={“admin”, “user”}, logical= Logical.AND)
@RequiresPermissions(value={“user:select”, “user:all”}, logical= Logical.OR)
若使用注解授权时需注入以下Bean
//自动代理
@Bean
@DependsOn({ "lifecycleBeanPostProcessor" })//指定BeanProcessor 接口
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
//通知器用来匹配所有加了认证注解的方法
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
例如:
4. RedisManager
RedisManager类是由Redis+Jedis+Spring封装而成的,用于操作缓存。
配置关于redis的基本信息
@Bean
public BaseRedisManager redisManager() {
if (this.redistype.equals("sentinel")) {
RedisSentinelManager redisSentinelManager;//redis哨兵管理器监控redis集群
(redisSentinelManager = new RedisSentinelManager()).setMasterName(this.master);
redisSentinelManager.setHost(this.nodes);
redisSentinelManager.setDatabase(this.databaseDefault);
redisSentinelManager.setPassword(this.password);
return redisSentinelManager;
}
RedisManager redisManager;
(redisManager = new RedisManager()).setHost(this.host);
redisManager.setPort(this.port);
redisManager.setPassword(this.password);
redisManager.setDatabase(this.databaseDefault);
return redisManager;
}
5. RedisCacheManager
缓存管理器,RedisCacheManager里实现了shiro中的CacheManager,重写了里面的getCache方法,又实现了Cache方法,重写了里面的get、put、remove等,整合redis的方法,用来实现缓存的持久化。
@Bean
public RedisCacheManager redisCacheManager() {
RedisCacheManager redisCacheManager;
(redisCacheManager = new RedisCacheManager()).setRedisManager( this.redisManager());
redisCacheManager.setPrincipalIdFieldName("id");//在redis缓存中用于标识用户的字段
return redisCacheManager;
}
6. SessionManager
SessionManager用于管理Shiro中的Session信息。Session也就是我们通常说的会话,会话是用户在使用应用程序一段时间内携带的数据。传统的会话一般是基于Web容器(如:Tomcat、EJB环境等)。Shiro提供的Session可以在任何环境中使用,不再依赖于其他容器。
SessionManager是shiro提供的完整的会话功能管理器。包括会话的创建、维护、删除、失效、验证等工作。使用redis存储管理session
@Bean
public SessionManager sessionManager() {
DefaultWebSessionManager defaultWebSessionManager;
(defaultWebSessionManager = new DefaultWebSessionManager()).setDeleteInvalidSessions(true);//删除过期的session
defaultWebSessionManager.setSessionDAO( this.sessionDAO());
return defaultWebSessionManager;
}
7. RedisSessionDAO
RedisSessionDAO的作用是为Session提供CRUD并进行持久化的一个shiro组件
1、AbstractSessionDAO 提供了 SessionDAO 的基础实现,如生成会话ID等。
2、CachingSessionDAO 提供了对开发者透明的会话缓存的功能,需要设置相应的 CacheManager。
3、MemorySessionDAO 直接在内存中进行会话维护。
4、EnterpriseCacheSessionDAO 提供了缓存功能的会话维护,默认情况下使用 MapCache 实现,内部使用 ConcurrentHashMap 保存缓存的会话。
5、RedisSessionDao提供了整合redis的会话缓存功能,只需要设置相应的redisManager。
在doCreate(Session)的时候,会获取一个SessionId,SessoinId是由其属性 sessionIdGenerator 来提供,所以需要配置sessionIdGenerator
@Bean
public RedisSessionDAO sessionDAO() {
RedisSessionDAO redisSessionDAO;
(redisSessionDAO = new RedisSessionDAO()).setRedisManager( this.redisManager());
redisSessionDAO.setSessionIdGenerator( this.sessionIdGenerator());//设置sessionId
return redisSessionDAO;
}
JavaUuidSessionIdGenerator
自动生成会话id,生成策略:UUID
@Bean
public JavaUuidSessionIdGenerator sessionIdGenerator() {
return new JavaUuidSessionIdGenerator();
}
8.2 Realm
自定义Reaml是继承了AuthorizingRealm这个抽象类,它下面是各种操作数据库的实现。
public class UserRealm extends AuthorizingRealm {
@Resource
private UserMapper userMapper;
@Resource
private UserRoleRelationMapper userRoleRelationMapper;
@Resource
private RolePermissionRelationMapper rolePermissionRelationMapper;
@Resource
private RoleMapper roleMapper;
@Resource
private PermissionMapper permissionMapper;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
User user = (User)principalCollection.getPrimaryPrincipal();
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
//查询用户的角色
Set<String> roles = roleMapper.queryAllById(user.getId());
info.setRoles(roles);
//查询用户的权限
Set<String> permissionSet = permissionMapper.queryAllByPermission(user.getId());
info.setStringPermissions(permissionSet);
//返回认证信息
return info;
}
//认证
@SneakyThrows
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//用户名,密码,数据库中获取
UsernamePasswordToken token=(UsernamePasswordToken) authenticationToken;
String username=token.getUsername();
String password=new String(token.getPassword());
if (StringUtils.isEmpty(username) && (StringUtils.isEmpty(password)) ) {
throw new Exception("用户名或密码不能为空");
}
User user = userMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getUsername, token.getUsername()));
if(ObjectUtils.isEmpty(user)){
throw new Exception("没有该用户");
}
SimpleByteSource simpleByteSource = new SimpleByteSource(user.getSalt());
//SimpleAuthenticationInfo中传入参数:第一个参数可以是当前用户的对象,也可以是用户的姓名,这只是一个身份的表示。(此参数可以通过subject.getPrincipal()方法获取—获取当前记录的用户,从这个用户对象进而再获取一系列的所需要的属性。)第二个参数:传入的是从数据库中获取到的password,然后再与token中的password进行对比,匹配上了就通过,匹配不上就报异常。第三个参数:盐–用于加密密码对比。 若不需要,则可以设置为空 “ ”。第四个参数:当前realm的名字。
//返回认证信息进行密码认证
return new SimpleAuthenticationInfo(user,user.getPassword(),simpleByteSource,this.getName());
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律