【springcloud】--zuul+Shiro搭建

【springcloud】--zuul+Shiro搭建,第1张

【springcloud】--zuul+Shiro搭建

目录

一、说明二、代码实现三、基本组件完整架构实现四、zuul+Shiro实现

4.1、Shiro基本集成4.2、Shiro基本集成---身份验证4.3、Shiro基本集成---权限验证

一、说明

Zuul的主要功能是路由转发和过滤器。路由功能是微服务的一部分。

二、代码实现

引用jar包



    org.springframework.cloud
    spring-cloud-starter-netflix-zuul

application.properties配置

server.port=8081
spring.application.name=zuul
eureka.client.service-url.defaultZone= http://localhost:8083/eureka/
#zuul
#以eurekaClient2开头的请求都转发到eurekaClient2
zuul.routes.api-a.path=/eurekaClient1
    @Override
    public String filterType() {
        return "pre";
    }
    @Override
    public int filterOrder() { //数字越小表示顺序越高,越先执行
        return 0;
    }
    @Override
    public boolean shouldFilter() {
        return true; //表示是否需要执行该filter,true表示执行,false表示不执行
    }
    
    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
//        System.out.println(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
        String accessToken = request.getParameter("token"); // 获取请求的参数
        if(StringUtils.isNotBlank(accessToken)) {
            //进行路由
            ctx.setSendZuulResponse(true);
            ctx.setResponseStatusCode(200);
            ctx.set("isSuccess", true);
        }else{
            //不进行路由
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(400);
            ctx.setResponseBody("token is empty");
            ctx.set("isSuccess", false);
        }
        return null;
    }
}

没带token参数时返回

带token参数时正常访问后面的系统服务

三、基本组件完整架构实现

现在有Eureka Server,端口8083;
有Zuul【zuul】,端口8081;
有Eureka Client【EurekaClient2】,开启分布式,端口分别是7081、7082;
有Eureka Client【EurekaClient1】,端口分别是7080;

现在有EurekaClient2服务可以直接访问接口:http://localhost:7081/getPersonById.do?id=1

现在有EurekaClient1服务可以直接访问接口【这个接口会通过ribbon负载均衡调用EurekaClient2服务----ribbon的实现】:
http://localhost:7080/ribbonPersonById.do

现在通过zuul服务来路由转发:
http://localhost:8081/eurekaClient1/ribbonPersonById.do



http://localhost:8081/EurekaClient2/getPersonById.do?id=1

综合结论:
从上面可以看出,单独的Eureka Client也是可以进行访问的,另外Eureka Client之间可以通过ribbon实现调用。
当有zuul组件时,任何请求都是通过zuul组件所在服务系统进行路由转发到对应Eureka Client上。
因此,上面的请求也反映了zuul组件的功能。

四、zuul+Shiro实现

从上面的简单实例,一个完整的系统平台,相关请求都是经过zuul来进行转发路由,因此在实际中“登录鉴权管理”也应该在zuul服务系统上进行构建,这里就介绍zuul+Shiro实现。

4.1、Shiro基本集成

引用jar包



    org.apache.shiro
    shiro-spring
    1.4.0


Shiro的config配置类

@Configuration
public class ShiroConfig {
    @Bean
    public MyShiroRealm customRealm() {
        //用户数据和Shiro数据交互的桥梁。比如需要用户身份认证、权限认证。都是需要通过Realm来读取数据。
        MyShiroRealm customRealm = new MyShiroRealm();
        return customRealm;
    }
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
        defaultSecurityManager.setRealm(customRealm());
        return defaultSecurityManager;
    }
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean factoryBean = new MyShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager);
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        factoryBean.setLoginUrl("/login");  //这是当任何url时,都进入/login界面
        // 登录成功后要跳转的连接
        factoryBean.setSuccessUrl("/index");   //当验证成功后,会进入/index
        //未授权界面、权限不足跳转页面,跳转到403
        factoryBean.setUnauthorizedUrl("/403");  //对应MyShiroRealm类的返回null时,自动跳转到/403
        //加载ShiroFilter权限控制规则
        loadShiroFilterChain(factoryBean);
        return factoryBean;
    }
    
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }
    
    private void loadShiroFilterChain(ShiroFilterFactoryBean factoryBean) {

        Map filterChainMap = new linkedHashMap();
        
        filterChainMap.put("/login", "anon");
        // filterChainMap.put("/register", "anon");
        //配置退出  过滤器
        filterChainMap.put("/logout", "logout");
        //剩余请求需要身份认证
        filterChainMap.put("
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        //查询mysql获取用户信息  通过浏览器提交的用户名去数据库查找相应的用户信息
        UserPo userInfo = userInfoService.selectUser(token.getUsername());
        if(userInfo != null){
            SecurityUtils.getSubject().getSession().setAttribute("user", userInfo);
            // 若存在,将此用户存放到登录认证info中,无需自己做密码对比,Shiro会为我们进行密码对比校验
            String userloginName= userInfo.getUserName();
            String userPasswd=userInfo.getPassWord();
            String name=getName();   //realm name
            return new SimpleAuthenticationInfo(userloginName, userPasswd, name);
        }
        return null;
    }
}

------从上面代码看出,通过传入的用户名称去查DB,然后和传来的登录密码对比,相同就说明身份验证通过。

模拟测试

@Controller
public class ShiroController {
//    @Autowired
//    private SessionDAO sessionDao;

    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String loginForm(Model model) {
        model.addAttribute("user", new UserPo());
        return "login";
    }

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public String login(UserPo user) {
        String username = user.getUserName();
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), user.getPassWord(), false);

        // 获取当前的Subject, 代表当前正在执行 *** 作的用户
        Subject currentUser = SecurityUtils.getSubject();
        try {
            currentUser.login(token);
            System.out.println("对用户[" + username + "]进行登录验证..验证通过");
        } catch (UnknownAccountException uae) {
            System.out.println("对用户[" + username + "]进行登录验证..验证未通过,未知账户");
        } catch (IncorrectCredentialsException ice) {
            System.out.println("对用户[" + username + "]进行登录验证..验证未通过,错误的凭证");
        } catch (LockedAccountException lae) {
            System.out.println("对用户[" + username + "]进行登录验证..验证未通过,账户已锁定");
        } catch (ExcessiveAttemptsException eae) {
            System.out.println("对用户[" + username + "]进行登录验证..验证未通过,错误次数过多");
        } catch (AuthenticationException ae) {
            // 通过处理Shiro的运行时AuthenticationException就可以控制用户登录失败或密码错误时的情景
            System.out.println("对用户[" + username + "]进行登录验证..验证未通过,堆栈轨迹如下");
            ae.printStackTrace();
        }
        // 验证是否登录成功
        if (currentUser.isAuthenticated()) {
            System.out.println("用户[" + username + "]登录认证通过(这里可以进行一些认证通过后的一些系统参数初始化 *** 作)");
            UserPo user2 = (UserPo) SecurityUtils.getSubject().getSession().getAttribute("user");
            return "index";
        } else {
            token.clear();
            return "login";
        }
    }

首先,任意没登录请求来访问都是跳转到登录页面
http://localhost:8081/login

登录成功后,跳转到成功页面。那么,后面直接访问zuul服务的接口都正常访问。
这里zuul主要是做路由转发的,那么事先没登录的访问去访问EurekaClient1,也是先跳转Zuul服务的登录系统,然后接口才能访问。

4.3、Shiro基本集成—权限验证

前面已经说明,当符合身份登录的继续访问,但是不同用户身份的权限又不一样,可以通过不同权限来解决请求是否访问成功。
首先前面Shiro配置类有lifecycleBeanPostProcessor()方法是开启“Shiro权限注解”,那么可以通过注解方法来确定不同类或方法的权限。
还是MyShiroRealm 类增加如下配置内容。

public class MyShiroRealm extends AuthorizingRealm {
    @Autowired
    UserInfoServiceImpl userInfoService;
    
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String loginName = (String) super.getAvailablePrincipal(principalCollection);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //查询mysql获取用户信息
        UserPo userInfo = userInfoService.selectUser(loginName);
        if(userInfo != null){
            info.setStringPermissions(userInfoService.userPerm(loginName));//用户对应的所有权限
            SecurityUtils.getSubject().getSession().setAttribute("user", userInfo);
        }
        return info;
    }

分析:userInfoService.userPerm(loginName)方法就是查数据库来获取这个登录用户所赋给的权限。

模拟测试,假设给用户设置user:show、user:admin两个权限,那么

@Override
public Set userPerm(String userName) {
    Set permSet = new HashSet<>();
    //配置的权限,模拟查库
    permSet.add("user:show");
    permSet.add("user:admin");
    return permSet;
}

为两个PersonController类、HelloWorldController类配置权限

@Controller
@RequestMapping("/basic")
@RequiresPermissions("user:admin") //这个类下面的方法拥有的权限
public class HelloWorldController {

	//http://localhost:8084/basic/sayHello.do
	@RequestMapping("/sayHello.do")
	@ResponseBody //这是返回json格式
	public Object sayHello() {
		return "Hello,World!";
	}

	@RequestMapping("/wrong.html")
	public Object wrongPage(){
		return "wrong";
	}
}
@Controller
@RequestMapping("/person")
@RequiresPermissions("user:person")
public class PersonController {

    @Autowired
    private PersonServiceImpl personServiceImpl;
    //http://localhost:8084/person/getPersonById.do
    @RequestMapping("/getPersonById.do")
    @ResponseBody
    public Object getPersonById(){
        PersonPo person = personServiceImpl.getPersonById(1);
        return JSON.toJSONString(person);
    }
}

直接请求的效果

以上就基本完成Zuul整合Shiro的功能。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5713295.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存