shiro很适合用来解决登录认证与权限控制
shiro就是过滤器做请求拦截,拦截url,redis里面存session,代表是支持分布式的,
session是用户信息,
依赖如下:
<!-- shiro插件 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.2</version>
</dependency>
<!-- shiro+redis缓存插件 -->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>2.4.2.1-RELEASE</version>
</dependency>
搭建步骤:
现在主流是和SpringBoot项目整合
@Configuration
public class ShiroConfig {
@Value("${shiro.session.timeout}")
private Long globalSessionTimeout;
@Value("${spring.redis.host}")
private String redisHost;
@Value("${spring.redis.port}")
private Integer redisPort;
@Autowired
private OnsAppService appService;
//@Bean
public TokenFilter tokenFilter(){
TokenFilter f=new TokenFilter();
return f;
};
public FeignSimpleFilter feignSimpleFilter() {
return new FeignSimpleFilter();
}
//@Bean
public SignatureFilter signatureFilter(){
return new SignatureFilter();
};
//@Bean
private ApiFilter apiFilter(){
ApiFilter a=new ApiFilter();
a.setAppService(appService);
return a;
}
@Bean
public ShiroDBRealm shiroDBRealm(){
return new ShiroDBRealm();//此处自己实现了shiro的认证方法,后续有介绍
};
@Bean
public DefaultWebSecurityManager securityManager(){
DefaultWebSecurityManager d=new DefaultWebSecurityManager();
d.setSessionManager(sessionManager());
d.setRealm(shiroDBRealm());
d.setCacheManager(cacheManager());
return d;
}
public CacheManager cacheManager(){
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
@Bean
public DefaultWebSessionManager sessionManager(){
DefaultWebSessionManager dwsm=new DefaultWebSessionManager();
dwsm.setGlobalSessionTimeout(globalSessionTimeout);
//dwsm.setGlobalSessionTimeout(6000);
dwsm.setSessionDAO(redisSessionDAO());
dwsm.getSessionIdCookie().setName("JSID");
return dwsm;
}
@Bean
public RedisSessionDAO redisSessionDAO(){
RedisSessionDAO rs=new RedisSessionDAO();
rs.setRedisManager(redisManager());
return rs;
}
@Bean
public RedisManager redisManager(){
RedisManager rm=new RedisManager();
rm.setHost(redisHost);
//rm.setExpire(globalSessionTimeout.intValue()/1000);
rm.setExpire(60);
rm.setPort(redisPort);
return rm;
}
@Bean
public ShiroFilterFactoryBean shiroFilter(){
ShiroFilterFactoryBean sff=new ShiroFilterFactoryBean();
sff.setSecurityManager(securityManager());
sff.setLoginUrl("/login");
sff.setSuccessUrl("/home");
sff.setUnauthorizedUrl("/login");
Map<String,Filter> filters=new HashMap<>();
//filters.put("tkf", tokenFilter());
filters.put("snf", signatureFilter());
filters.put("af", apiFilter());
filters.put("feignFilter", feignSimpleFilter());
ItsmSpiFilter a = new ItsmSpiFilter();
// a.setAppService(appService);
filters.put("spi", a);
RestTokenFilter restTokenFilter = new RestTokenFilter();
filters.put("rest", restTokenFilter);
sff.setFilters(filters);
Map<String,String> filterChain = new LinkedHashMap<>();
filterChain.put("/favicon.ico", "anon");
filterChain.put("/login", "anon");
filterChain.put("/updatePwd", "anon");
filterChain.put("/toLogin", "anon");
filterChain.put("/captcha.png", "anon");
filterChain.put("/logout", "logout");
filterChain.put("/static/**", "anon");
filterChain.put("/forgetPwd/**", "anon");
filterChain.put("/cxf/**", "anon");
filterChain.put("/ras-itsm/authentication", "anon");
filterChain.put("/ras-itsm/**", "anon,af");
filterChain.put("/pocOrderApi/**", "anon");
filterChain.put("/itsm-spi/**", "anon,spi");
//filterChain.put("/ras-itsm/**", "anon");
filterChain.put("/itsm-spi/v1/**", "anon,snf");
filterChain.put("/weixin/**", "anon");
filterChain.put("/rmsPern/valiDate*", "anon");
filterChain.put("/rmsPern/registerUser.do", "anon");
filterChain.put("/rmsPern/emailConfirmUser", "anon");
filterChain.put("/rmsPern/itsm/savePerson", "anon");
filterChain.put("/api/oauth2/**", "anon");
filterChain.put("/api/**", "anon,af");
filterChain.put("/syncData/**", "anon,af");
filterChain.put("/restApi/getToken", "anon");
//filterChain.put("/restApi/**", "anon");
filterChain.put("/restApi/**", "anon");
filterChain.put("/org/queryOrgBranchListByCost", "anon,feignFilter");
filterChain.put("/feign/**", "anon,feignFilter");
filterChain.put("/**", "authc");
sff.setFilterChainDefinitionMap(filterChain);
return sff;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
@Bean
public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
return new LifecycleBeanPostProcessor();
}
shiro的redisMangeer对session和权限数据的缓存时长都有影响,当我们刷新页面session的缓存有效时长会刷新为最长时效,但是权限数据缓存不会随之刷新,权限数据缓存到期后,session时间若没有失效则会自动重新加载权限数据到缓存中,若session失效则需要重新登录,登录完成后session与权限的过期时间全部更新为最新时效
配置类中的Realm
查询出用户信息与用户角色权限信息
public class ShiroDBRealm extends AuthorizingRealm {
private Logger log = LoggerFactory.getLogger(ShiroDBRealm.class);
@Autowired
private UserService userService;
@Autowired
private SysResourceService sysResourceService;
@Autowired
private StringRedisTemplate stringTpl;
@Autowired
private OAuthService oauthService;
//功能权限中更改的部分在这能查出来,系统权限和项目权限都加到了info中
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set<String> roles = new HashSet<String>();
Set<String> permissions = new HashSet<String>();
SysUser user = (SysUser) principals.getPrimaryPrincipal();
log.info("用户 :account:[{}],userId:[{}] 进行权限认证 !", user.getAccount(), user.getId());
List<SysRole> sysRoles = userService.getUserRoles(user.getId());
for (SysRole r : sysRoles) {
roles.add(r.getRoleCode());
List<SysResource> sysResources = userService.getRoleResourcese(r.getRoleId());
log.info("用户 :权限:[{}] 进行权限认证 !", sysResources.stream().map(it -> it.getCode()).collect(Collectors.toList()));
for (SysResource re : sysResources) {
permissions.add(re.getCode());
}
}
info.addRoles(roles);
info.addStringPermissions(permissions);
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
List<Map<String, Object>> menus = null;
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
//SysUser user = userService.getUserByName(token.getUsername());
String password = String.valueOf(token.getPassword());
String client=token.getHost()==null?Project.UNKNOW.name():token.getHost();
SysUser user=oauthService.authUsers(token.getUsername(), password, client);
user.setPassword(password);
SimpleAuthenticationInfo sai = new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
String projectCode = stringTpl.boundValueOps(token.getUsername() + Project.class.getName()).get();
switch (Project.valueOf(projectCode)) {
case RAS:
menus = sysResourceService.findResourceByUser(user, Project.RAS);
break;
case RMS:
menus = sysResourceService.findResourceByUser(user, Project.RMS);
break;
case RAS_WEIXIN:
case RAS_MANAGER:
break;
default:
menus = sysResourceService.findResourceByUser(user, Project.RAS);
break;
}
String strMenu = JacksonUtil.getJson(menus);
stringTpl.boundValueOps(user.getAccount() + projectCode + "_menu").set(strMenu);
return sai;
}
}
到此为止便可以完成登录的校验
也可以使用如下注解来完成对权限按钮的校验