前面文章发了集成redis和dubbo zookeeper的配置 ,这篇文章讲一下怎么集成shiro和mq。本章内容不讲解原理性的东西。请自己百度。
上章地址:http://www.cnblogs.com/MrLimy/p/8342127.html
首先打开demo父工程,把需要的依赖加进去。由于我前面依赖过log4j的jar包,这里如果依赖activemq-all会和前面依赖的log4j冲突所以我这里依赖除了log4j的jar包。在官网可以查看mq依赖。
<!-- shiro开始 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>${shiro.version}</version> </dependency> <!-- shiro结束 --> <!-- activemq --> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-broker</artifactId> <version>5.11.1</version> </dependency> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-client</artifactId> <version>5.11.1</version> </dependency> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-kahadb-store</artifactId> <version>5.11.1</version> </dependency> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-spring</artifactId> <version>5.11.1</version> </dependency> <dependency> <groupId>org.fusesource.hawtbuf</groupId> <artifactId>hawtbuf</artifactId> <version>1.11</version> </dependency> <!-- activemq --> <!-- jms --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> <version>${spring.version}</version> </dependency> <!-- jms -->
打开demo_web工程的web.xml文件,在上章配置的基础上增加shiro.xml和activemq.xml的初始化和shiro过滤器。
web.xml:
<context-param> <param-name>contextConfigLocation</param-name> <!-- xml配置文件交给Spring监听器初始化 --> <param-value>classpath:application.xml;classpath:shiro.xml;classpath:activemq.xml</param-value> </context-param> <!-- shiro --> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <async-supported>true</async-supported> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- shiro -->
shiro.xml:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd"> <!-- Realm实现 --> <bean id="userRealm" class="com.ngt.util.Realm" /> <!-- 安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="userRealm" /> </bean> <!-- Shiro的Web过滤器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- Shiro的核心安全接口,这个属性是必须的 --> <property name="securityManager" ref="securityManager" /> <!-- 身份认证失败,则跳转到登录页面的配置 --> <property name="loginUrl" value="/" /> <!-- 权限认证失败,则跳转到指定页面 --> <property name="unauthorizedUrl" value="/" /> <!-- Shiro连接约束配置,即过滤链的定义 --> <!-- 需要注意filterChainDefinitions过滤器中对于路径的配置是有顺序的, 当找到匹配的条目之后容器不会再继续寻找。 因此带有通配符的路径要放在后面。三条配置的含义是: /authc/admin需要用户有用admin权限、 /authc/**用户必须登录才能访问、 /**其他所有路径任何人都可以访问。 --> <property name="filterChainDefinitions"> <value> /authc/admin = roles[admin] <!--authc表示需要认证 没有进行身份认证是不能进行访问的--> /authc/** = authc <!--anon 表示匿名访问,不需要认证以及授权--> /** = anon </value> </property> </bean> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> </beans>
com.ngt.util.RealmL: 继承AuthorizingRealm类,重写doGetAuthorizationInfo(权限验证) 和doGetAuthenticationInfo(登陆验证,subject.login执行后会执行此方法) 方法
原理自己百度一下,很多。本章只用doGetAuthenticationInfo方法。
package com.ngt.util; import java.util.Set; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; 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 com.ngt.entity.User; public class Realm extends AuthorizingRealm{ /** * 用于用户filterChainDefinitions权限验证 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) { String userName=arg0.getPrimaryPrincipal().toString(); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //角色 Set<String> roleName=null; //角色拥有的权限 Set<String> permissionName=null; roleName.add("这里是拥有的角色1"); roleName.add("这里是拥有的角色2"); roleName.add("这里是拥有的角色3"); permissionName.add("这里是拥有的权限1"); permissionName.add("这里是拥有的权限2"); permissionName.add("这里是拥有的权限3"); info.setRoles(roleName); info.setStringPermissions(permissionName); return info; } /** * 首先执行,用于登陆验证 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException { //这里只需要从tonken中获取到登陆账号查询出登陆账号的密码放入 AuthenticationInfo String userLoginName= arg0.getPrincipal().toString(); User user=queryUserByLoginName(userLoginName); if(user!=null){ // 将查询到的用户账号和密码存放到 authenticationInfo用于后面的权限判断。第三个参数传入realName。 AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUserName(),user.getUserPassword(),user.getId()); return authenticationInfo; }else{ //这里可以自定义一个登陆账户不存在的异常在在控制器调用的时候捕获这个异常 return null; } } private User queryUserByLoginName(String userLoginName) { User user=new User(); if("tom".equals(userLoginName)){ user.setUserName("tom"); user.setUserPassword("123123"); user.setId("SB001"); return user; }else{ return null; } } }
User实体类:
package com.ngt.entity; public class User { private String id; private String userName; private String userPassword; private String userEmail; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserPassword() { return userPassword; } public void setUserPassword(String userPassword) { this.userPassword = userPassword; } public String getUserEmail() { return userEmail; } public void setUserEmail(String userEmail) { this.userEmail = userEmail; } }
前台页面就不提供了自己写个标签<a href="/login/tom/123123">shiro验证</a>点击进入控制器,这里提供一下控制器登陆时的写法。
package com.ngt.controller; import java.util.HashMap; import java.util.Map; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class ShiroTestController { /** * shiro登陆验证 * @return */ @RequestMapping("/login/{loginName}/{loginPwd}") public @ResponseBody Map<String, String> login(@PathVariable("loginName")String loginName,@PathVariable("loginPwd")String loginPwd){ Map<String, String> map=new HashMap<>(); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(loginName,loginPwd); try { subject.login(token); map.put("rs", "登陆成功"); return map; } catch (Exception e) { map.put("rs", "登陆失败"); return map; } } }
配置activemq需要先下载apache-activemq我这里用的版本是5.11.1。解压启动D:\apache-activemq-5.11.1\bin\win64\wrapper.exe。访问http://127.0.0.1:8161/admin/
用户密码都是admin。
activemq.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:amq="http://activemq.apache.org/schema/core" xmlns:jms="http://www.springframework.org/schema/jms" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.11.1.xsd"> <!-- ActiveMQ 连接工厂 --> <!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供 --> <!-- 如果连接网络:tcp://ip:61616;未连接网络:tcp://localhost:61616 以及用户名,密码 --> <amq:connectionFactory id="amqConnectionFactory" brokerURL="tcp://localhost:61616" userName="admin" password="admin" /> <!-- Spring Caching连接工厂 --> <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory --> <bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory"> <!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory --> <property name="targetConnectionFactory" ref="amqConnectionFactory"></property> <!-- 同上,同理 --> <!-- <constructor-arg ref="amqConnectionFactory" /> --> <!-- Session缓存数量 --> <property name="sessionCacheSize" value="100" /> </bean> <!-- Spring JmsTemplate 的消息生产者 start --> <!-- 定义JmsTemplate的Queue类型 --> <bean id="jmsQueueTemplate" class="org.springframework.jms.core.JmsTemplate"> <!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 --> <constructor-arg ref="connectionFactory" /> <!-- 非pub/sub模型(发布/订阅),即队列模式 --> <property name="pubSubDomain" value="false" /> </bean> <!-- 定义JmsTemplate的Topic类型 --> <bean id="jmsTopicTemplate" class="org.springframework.jms.core.JmsTemplate"> <!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 --> <constructor-arg ref="connectionFactory" /> <!-- pub/sub模型(发布/订阅) --> <property name="pubSubDomain" value="true" /> </bean> <!--Spring JmsTemplate 的消息生产者 end --> <!-- 消息消费者 start --> <!-- 定义Queue监听器 --> <jms:listener-container destination-type="queue" container-type="default" connection-factory="connectionFactory" acknowledge="auto"> <jms:listener destination="test.queue.simple" ref="con" method="receiveMessage" /> <jms:listener destination="test.queue.simple" ref="con" method="receiveMessage2" /> <jms:listener destination="test.queue.object" ref="con" method="receiveObject" /> </jms:listener-container> <!-- 定义Topic监听器 --> <jms:listener-container destination-type="topic" container-type="default" connection-factory="connectionFactory" acknowledge="auto"> <jms:listener destination="test.topic.simple" ref="con" method="receiveTopicMessage" /> <jms:listener destination="test.topic.simple" ref="con" method="receiveTopicMessage2" /> </jms:listener-container> <!-- 消息消费者 end --> </beans>
activemq消息产生者:
package com.ngt.service.impl; import javax.annotation.Resource; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.Session; import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.core.MessageCreator; import org.springframework.stereotype.Service; import com.ngt.domain.TestMessage; import com.ngt.service.ProducerService; @Service("pro") public class ProducerServiceImpl implements ProducerService{ //这里实现的是队列 点对点发消息 所以注入的bean 与activemq.xml里的队列id保持一致 @Resource(name="jmsQueueTemplate") private JmsTemplate jmsQueueTemplate; @Resource(name="jmsTopicTemplate") private JmsTemplate jmsTopicTemplate; @Override public void sendMessage(String destinationName, final String message) { System.out.println("生产方发送Queue字符串消息:"+message); jmsQueueTemplate.send(destinationName, new MessageCreator() { @Override public Message createMessage(Session session) throws JMSException { return session.createTextMessage(message); } }); } @Override public void sendMessage(String destinationName,TestMessage testMessage) { System.out.println("生产方发送Queue对象消息:"+testMessage); jmsQueueTemplate.convertAndSend(destinationName, testMessage); } @Override public void sendTopicMessage(String destinationName, String message) { System.out.println("生产方发送Topic文本消息:"+message); jmsTopicTemplate.convertAndSend(destinationName, message); } }
activemq消息消费者:
package com.ngt.service.impl; import org.springframework.stereotype.Service; import com.ngt.domain.TestMessage; import com.ngt.service.ConsumerService; @Service("con") public class ConsumerServiceImpl implements ConsumerService { public void receiveMessage(String message) { System.out.println("消费方接收消息1:"+message); } public void receiveMessage2(String message) { System.out.println("消费方接收消息2:"+message); } public void receiveObject(TestMessage testMessage) { System.out.println("消费方接收对象:"+ testMessage); } public void receiveTopicMessage(String message) { System.out.println("topic消费方接收消息1:"+message); } public void receiveTopicMessage2(String message) { System.out.println("topic消费方接收消息2:"+message); } }
ActiveMqController:
package com.ngt.controller; import java.util.Map; import javax.annotation.Resource; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.ngt.domain.TestMessage; import com.ngt.service.ProducerService; @Controller public class ActiveMqController { @Resource(name="pro") private ProducerService producerService; @RequestMapping("/queueText/{destinationName}/{message}") public @ResponseBody Map<String, String> queueText(@PathVariable("destinationName")String destinationName,@PathVariable("message")String message){ producerService.sendMessage(destinationName, message); return null; } @RequestMapping("/queueObject/{destinationName}") public @ResponseBody Map<String, String> queueObject(@PathVariable("destinationName")String destinationName){ TestMessage testMessage=new TestMessage(); testMessage.setId(1); testMessage.setMsg("对象消息。。。"); producerService.sendMessage(destinationName, testMessage); return null; } @RequestMapping("/topicText/{destinationName}/{message}") public @ResponseBody Map<String, String> topicText(@PathVariable("destinationName")String destinationName,@PathVariable("message")String message){ producerService.sendMessage(destinationName, message); return null; } }
消息接收者是被监听器监听的,发送消息后只需要看控制台打印即可。