springcloud zuul shiro网关鉴权并向服务传递用户信息
1.pom文件
<dependencies> <!--eureka客户端--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--zuul网关--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>com.ihrm</groupId> <artifactId>ihrm_common</artifactId> <version>1.0-SNAPSHOT</version> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </exclusion> </exclusions> </dependency>
2.将shiro配置移至网关
package com.ihrm.gate.shiro.config; import com.ihrm.common.shiro.CustomSessionManager; import com.ihrm.common.shiro.IhrmRealm; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.crazycake.shiro.RedisCacheManager; import org.crazycake.shiro.RedisManager; import org.crazycake.shiro.RedisSessionDAO; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; @Configuration public class ShiroConfig { @Bean public IhrmRealm ihrmRealm() { return new IhrmRealm(); } @Bean public DefaultWebSecurityManager getSecurityManager(DefaultWebSessionManager sessionManager, RedisCacheManager cacheManager) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(ihrmRealm()); //将自定义的会话管理器注册到安全管理器中 securityManager.setSessionManager(sessionManager); //将自定义的redis缓存管理器注册到安全管理器中 securityManager.setCacheManager(cacheManager); return securityManager; } @Bean public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) { //1.创建过滤器工厂 ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean(); //2.设置安全管理器 filterFactory.setSecurityManager(securityManager); //3.通用配置(跳转登录页面,未授权跳转的页面) filterFactory.setLoginUrl("/autherror?code=1");//跳转url地址 filterFactory.setUnauthorizedUrl("/autherror?code=2");//未授权的url //4.设置过滤器集合 Map<String,String> filterMap = new LinkedHashMap<>(); //anon -- 匿名访问 filterMap.put("/sys/login","anon"); filterMap.put("/autherror","anon"); //注册 //authc -- 认证之后访问(登录) filterMap.put("/**","authc"); //perms -- 具有某中权限 (使用注解配置授权) filterFactory.setFilterChainDefinitionMap(filterMap); return filterFactory; } /** * 再web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制 * */ @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private Integer port; /** * 1.redis的控制器,操作redis */ public RedisManager redisManager() { RedisManager redisManager = new RedisManager(); redisManager.setHost(host+ ":" + port); return redisManager; } /** * 2.sessionDao */ public RedisSessionDAO redisSessionDAO() { RedisSessionDAO sessionDAO = new RedisSessionDAO(); sessionDAO.setRedisManager(redisManager()); return sessionDAO; } /** * 3.会话管理器 */ @Bean public DefaultWebSessionManager sessionManager() { CustomSessionManager sessionManager = new CustomSessionManager(); //shiro 的session默认放在cookie中 禁用 sessionManager.setSessionIdCookieEnabled(false); //禁用url 重写 url; shiro请求时默认 jsessionId=id sessionManager.setSessionIdUrlRewritingEnabled(false); sessionManager.setSessionDAO(redisSessionDAO()); return sessionManager; } /** * 4.缓存管理器 */ @Bean public RedisCacheManager cacheManager() { RedisCacheManager redisCacheManager = new RedisCacheManager(); redisCacheManager.setRedisManager(redisManager()); return redisCacheManager; } //开启对shior注解的支持 @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; } }
3.realm,此realm只负责鉴权不负责认证
package com.ihrm.common.shiro; import com.ihrm.domain.system.response.ProfileResult; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import java.util.Set; public class IhrmRealm extends AuthorizingRealm { @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { ProfileResult result = (ProfileResult)getAvailablePrincipal(principalCollection); Set<String> apis = (Set<String>)result.getRoles().get("apis"); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.setStringPermissions(apis); return info; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { return null; } }
4.启动类
package com.ihrm.gate; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication(scanBasePackages = "com.ihrm") @EnableDiscoveryClient @EnableZuulProxy public class GateApplication { public static void main(String[] args) { SpringApplication.run(GateApplication.class,args); } }
5.配置信息
server: port: 9696 spring: application: name: ihrm-gate #指定服务名 eureka: client: service-url: defaultZone: http://localhost:6868/eureka instance: prefer-ip-address: true zuul: routes: ihrm-company: path: /company/** # url: http://127.0.0.1:9001 #映射路径对应的实际url地址 serviceId: ihrm-company stripPrefix: false sentiviteHeaders: #将指定路由的敏感头设置为空 customSensitiveHeaders: true #对指定路由开启自定义敏感头 ihrm-system: path: /sys/** # url: http://127.0.0.1:9001 #映射路径对应的实际url地址 serviceId: ihrm-system stripPrefix: false sentiviteHeaders: #将指定路由的敏感头设置为空 customSensitiveHeaders: true #对指定路由开启自定义敏感头
6.向服务传递登陆用户信息
package com.ihrm.gate.filter; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.google.gson.Gson; import com.ihrm.domain.system.response.ProfileResult; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.apache.shiro.SecurityUtils; import org.apache.shiro.codec.Base64; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.io.Serializable; import java.net.URLEncoder; @Component public class LoginFilter extends ZuulFilter { /** * pre: 在请求路径之前执行 * routing: 在请求路径时执行 * post: 在请求路径后执行 * error: 在请求发生错误时执行 * @return */ public String filterType() { return "pre"; } /** * 优先级为0,数字越大,优先级越低 * @return */ public int filterOrder() { return 0; } /** *是否执行该过滤器,此处为true,说明需要过滤 * 一些不需要过滤的请求返回false * @return */ public boolean shouldFilter() { return true; } /** * 过滤器执行的具体逻辑 * @return * @throws ZuulException */ @SneakyThrows public Object run() throws ZuulException { // System.out.println("LoginFilter执行了"); RequestContext context = RequestContext.getCurrentContext(); HttpServletRequest req = context.getRequest(); Subject subject = SecurityUtils.getSubject(); //获取用户信息 PrincipalCollection principals = subject.getPrincipals(); if (principals != null) { ProfileResult result = (ProfileResult) principals.getPrimaryPrincipal(); //将用户信息json并编码(不编码服务取出时中文乱码) String resultJson = new Gson().toJson(result); resultJson = new String(Base64.encode(resultJson.getBytes("UTF-8"))); //放入 context.addZuulRequestHeader("key1", resultJson); } return null; } }
7.服务获取用户信息
HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String userJson = req.getHeader("key1"); userJson = new String(Base64.decode(userJson.getBytes("UTF-8"))); result = new Gson().fromJson( userJson,ProfileResult.class);