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);

 

posted @ 2021-09-07 22:05  it_bky  阅读(948)  评论(0编辑  收藏  举报