1、web-controller层配置:
webapp:web.xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!--1.spring的web监听器-->
<!--1.1 修改监听器加载的路径-->
<!--这里必须配置classpath*: 因为要读取dao,service工程的applicationContext文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/applicationContext-*.xml</param-value>
</context-param>
<!--1.2 监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--2.字符编码过滤器-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--转换编码-->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--3.springmvc核心控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--读取springmvc.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
<!--项目启动立即加载配置-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--配置拦截的url规则,说明:
1.*.do,表示以.do结尾的请求进入前端控制器
2./,表示所有请求都进入前端控制器
-->
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!--4.shiro与spring整合-->
<!--DelegatingFilterProxy: 代理过滤器
作用:负责把需要认证和授权请求进行拦截,交给ShiroFilter的对象进行真正的认证和授权
指定ShiroFilter对象的方式
方式一:通过参数targetBeanName指定
<init-param>
<param-name>targetBeanName</param-name>
<param-value>abc</param-value>
</init-param>
方式二:直接把过滤器名称作为ShiroFilter对象名称
-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<!--在DelegatingFilterProxy对象消耗的时候,执行回收资源的动作(可选的配置)-->
<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>
</web-app>
springmvc.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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--1.扫描Controller所在包(不要去扫描service)-->
<context:component-scan base-package="cn.itcast.web"/>
<!--2.配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--2.1 页面前缀 注意:放在WEB-INF下用户不能直接访问页面,更加安全-->
<property name="prefix" value="/WEB-INF/pages/"/>
<!--2.2 页面后缀-->
<property name="suffix" value=".jsp"/>
</bean>
<!--3.mvc注解驱动-->
<mvc:annotation-driven conversion-service="conversionService"/>
<!--4.配置类型转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<!--配置自定义转换器-->
<property name="converters">
<set>
<bean class="cn.itcast.web.converter.StringToDateConverter"/>
</set>
</property>
</bean>
<!--5.配置自定义异常处理类-->
<bean class="cn.itcast.web.exceptions.CustomExceptionRosolver"/>
<!--6.开启AOP注解的支持-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!--7.Dubbo配置-->
<!--7.1指定服务名称-->
<dubbo:application name="export_web_manager"/>
<!--7.2zookeeper注册配置-->
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<!--7.3包扫描@Reference-->
<dubbo:annotation package="cn.itcast.web"/>
<!--文件上传相关配置-->
<!--8.加载七牛qiniu.properties-->
<context:property-placeholder location="classpath*:qiniu.properties"/>
<!--8.2开启springmvc文件上传组件-->
<!--注意:必须给id,而且名称必须叫multipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--限制文件大小5M-->
<property name="maxUploadSize" value="5242880"/>
</bean>
</beans>
applicationContext-dubbo.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:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--dubbo服务提供方-->
<!--1.dubbo的服务名称-->
<dubbo:application name="export_web_manager"/>
<!--2.注册中心的地址配置-->
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<!--3.扫描dubbo注解-->
<dubbo:annotation package="cn.itcast.web"/>
<!--手动远程注入dubbo服务,代替@Reference-->
<!--
dubbo:reference: 手动创建远程调用服务
interface: 远程服务的接口
id: 把远程服务引用对象放入IOC容器
-->
<dubbo:reference id="contractService" interface="cn.itcast.service.cargo.ContractService" timeout="100000"/>
</beans>
applicationContext-shiro.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd ">
<!--1.配置ShiroFilter对象,shiroFiter和web.xml配置filter-name保持一致-->
<!--ShiroFilterFactoryBean:处理用户的认证和授权的请求的对象-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!--注入SecurityManager-->
<property name="securityManager" ref="securityManager"/>
<!--修改shiro默认登录页面-->
<property name="loginUrl" value="/login.jsp"/>
<!--授权失败提示页面-->
<property name="unauthorizedUrl" value="/unauthorized.jsp"/>
<!--添加shiro的过滤器-->
<property name="filterChainDefinitions">
<!--
注意:
1)拦截路径问题
* : 拦截一层目录
**: 拦截任意层目录
2)默认登录页面问题
Shiro认证失败了,自动跳转到login.jsp页面,可以通过loginUrl参数修改默认登录页面
anon代表不认证也可以访问(匿名访问),通常对静态资源进行放行
authc代表必须通过认证才可以访问,通常对动态资源(controller,jsp页面)进行拦截
perms 过滤器,就是进行权限校验的过滤器
-->
<value>
/css/**=anon
/img/**=anon
/make/**=anon
/plugins/**=anon
/login.do=anon
/company/list.do=perms["企业管理"]
/system/module/list.do=perms["模块管理"]
/system/user/list.do=perms["用户管理"]
/system/dept/list.do=perms["部门管理"]
/system/role/list.do=perms["角色管理"]
/system/log/list.do==perms["日志管理"]
/**=authc
</value>
</property>
</bean>
<!--2.创建SecurityManager对象-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--注入Realm-->
<property name="realm" ref="myRealm"/>
</bean>
<!--3.创建自定义Realm对象:真正执行和授权逻辑的地方-->
<bean id="myRealm" class="cn.itcast.web.shiro.AuthRealm">
<!--注入凭证匹配器-->
<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
<!--4.创建凭证匹配器:只是普通加密,没有加盐的-->
<!--指定算法
md5: 使用md5算法进行登录
sha1: 使用sha1算法进行登录
-->
<!-- <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="md5"/>
</bean>-->
<!--5.创建自定义凭证匹配器:加盐加密-->
<bean id="credentialsMatcher" class="cn.itcast.web.shiro.CustomCredentialsMatcher"/>
<!--6.开启Shiro的注解功能(底层是AOP)-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
</beans>
applicationContext-rabbitmq-producer.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:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<!--1.创建连接工厂-->
<rabbit:connection-factory
id="connectionFactory"
host="127.0.0.1"
port="5672"
virtual-host="/heima"
username="heima"
password="123"
/>
<!--2.配置admin:管理连接与频道-->
<rabbit:admin connection-factory="connectionFactory"/>
<!--3.声明队列(创建队列)-->
<rabbit:queue name="contractQueue"/>
<!--4.创建交换机,同时把队列绑定到交换机上-->
<!--name:交换机的名称-->
<rabbit:topic-exchange name="contractTopicExchange">
<!--把队列绑定到交换机上-->
<rabbit:bindings>
<!--pattern: 指定routing key-->
<!--queue: 绑定的队列名称-->
<rabbit:binding pattern="contract.#" queue="contractQueue"></rabbit:binding>
</rabbit:bindings>
</rabbit:topic-exchange>
<!--5.创建RabbitTemplate对象:封装RabbitMQ的Api-->
<!--message-converter: 消息内容转换器-->
<rabbit:template id="rabbitTemplate"
connection-factory="connectionFactory"
exchange="contractTopicExchange"
message-converter="messageConverter"
/>
<!--6.配置Json转换器(MQ传输过程中,用于Java与Json字符串之间转换)-->
<bean id="messageConverter" class="org.springframework.amqp.support.converter.Jackson2JsonMessageConverter"/>
</beans>
applicationContext-task.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--1.创建任务对象-->
<bean id="contractTask" class="cn.itcast.web.task.ContractTask"/>
<!--2.创建JobDetail对象-->
<bean id="JobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<!--2.1 指定任务对象-->
<property name="targetObject" ref="contractTask"/>
<!--2.2 指定任务方法-->
<property name="targetMethod" value="execute"/>
</bean>
<!--3.创建Trigger触发去-->
<bean id="trigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="JobDetail"/>
<!--3.1任务表达式-->
<property name="cronExpression" value="0/5 * * * * ? *"/>
</bean>
<!--4.创建Scheduler任务调度对象-->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<array>
<ref bean="trigger"/>
</array>
</property>
</bean>
</beans>
工具类:
发送邮件:
import javax.mail.Address;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
/**
* 使用JavaMail发送邮件的工具类
*/
public class MailUtils {
/**
* 实现邮件发送的方法
*/
public static void sendMsg(String to,String subjet,String content) throws Exception {
// (一)建立和邮件发送服务的连接
// 1.设置邮件服务器的参数
Properties properties = new Properties();
properties.setProperty("mail.smtp.host","smtp.sina.com"); // 设置主机地址 smtp.qq.com smtp.sina.com
properties.setProperty("mail.smtp.auth","true"); // 认证
// 2. 产生一个用于邮件发送的Session对象
Session session = Session.getInstance(properties);
// (二) 编写一封邮件内容
// 3. 产生一个邮件的消息对象
MimeMessage message = new MimeMessage(session);
// 4. 设置消息的发送者
Address fromAddr = new InternetAddress("004@sina.com");
message.setFrom(fromAddr);
// 5. 设置消息的接收者
Address toAddr = new InternetAddress(to);
// to 直接发送 cc 抄送 bcc密送
message.setRecipient(MimeMessage.RecipientType.TO,toAddr);
// 6. 设置主题
message.setSubject(subjet);
// 7. 设置正文
message.setText(content);
// (三)发送邮件
// 8. 准备发送,得到火箭
Transport transport = session.getTransport("smtp");
// 9. 设置火箭的发射目标
transport.connect("smtp.sina.com","004@sina.com","loveyou");
// 10. 发送
transport.sendMessage(message,message.getAllRecipients());
// 11. 关闭
transport.close();
}
}
文件下载:
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class DownloadUtil {
/**
* @param filePath 要下载的文件路径
* @param returnName 返回的文件名
* @param response HttpServletResponse
* @param delFlag 是否删除文件
*/
protected void download(String filePath,String returnName,HttpServletResponse response,boolean delFlag){
this.prototypeDownload(new File(filePath), returnName, response, delFlag);
}
/**
* @param file 要下载的文件
* @param returnName 返回的文件名
* @param response HttpServletResponse
* @param delFlag 是否删除文件
*/
protected void download(File file,String returnName,HttpServletResponse response,boolean delFlag){
this.prototypeDownload(file, returnName, response, delFlag);
}
/**
* @param file 要下载的文件
* @param returnName 返回的文件名
* @param response HttpServletResponse
* @param delFlag 是否删除文件
*/
public void prototypeDownload(File file,String returnName,HttpServletResponse response,boolean delFlag){
// 下载文件
FileInputStream inputStream = null;
ServletOutputStream outputStream = null;
try {
if(!file.exists()) return;
response.reset();
//设置响应类型 PDF文件为"application/pdf",WORD文件为:"application/msword", EXCEL文件为:"application/vnd.ms-excel"。
response.setContentType("application/octet-stream;charset=utf-8");
//设置响应的文件名称,并转换成中文编码
//returnName = URLEncoder.encode(returnName,"UTF-8");
returnName = response.encodeURL(new String(returnName.getBytes(),"iso8859-1")); //保存的文件名,必须和页面编码一致,否则乱码
//attachment作为附件下载;inline客户端机器有安装匹配程序,则直接打开;注意改变配置,清除缓存,否则可能不能看到效果
response.addHeader("Content-Disposition", "attachment;filename="+returnName);
//将文件读入响应流
inputStream = new FileInputStream(file);
outputStream = response.getOutputStream();
int length = 1024;
int readLength=0;
byte buf[] = new byte[1024];
readLength = inputStream.read(buf, 0, length);
while (readLength != -1) {
outputStream.write(buf, 0, readLength);
readLength = inputStream.read(buf, 0, length);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
//删除原文件
if(delFlag) {
file.delete();
}
}
}
/**
* @param byteArrayOutputStream 将文件内容写入ByteArrayOutputStream
* @param response HttpServletResponse 写入response
* @param returnName 返回的文件名
*/
public void download(ByteArrayOutputStream byteArrayOutputStream, HttpServletResponse response, String returnName) throws IOException{
response.setContentType("application/octet-stream;charset=utf-8");
returnName = response.encodeURL(new String(returnName.getBytes(),"iso8859-1")); //保存的文件名,必须和页面编码一致,否则乱码
response.addHeader("Content-Disposition", "attachment;filename=" + returnName);
response.setContentLength(byteArrayOutputStream.size());
ServletOutputStream outputstream = response.getOutputStream(); //取得输出流
byteArrayOutputStream.writeTo(outputstream); //写到输出流
byteArrayOutputStream.close(); //关闭
outputstream.flush(); //刷数据
}
}
对象属性转换:
import org.springframework.cglib.beans.BeanMap;
import java.util.HashMap;
import java.util.Map;
public class BeanMapUtils {
/**
* 将对象属性转化为map结合
*/
public static <T> Map<String, Object> beanToMap(T bean) {
Map<String, Object> map = new HashMap<>();
if (bean != null) {
BeanMap beanMap = BeanMap.create(bean);
for (Object key : beanMap.keySet()) {
map.put(key+"", beanMap.get(key));
}
}
return map;
}
/**
* 将map集合中的数据转化为指定对象的同名属性中
*/
public static <T> T mapToBean(Map<String, Object> map,Class<T> clazz) throws Exception {
T bean = clazz.newInstance();
BeanMap beanMap = BeanMap.create(bean);
beanMap.putAll(map);
return bean;
}
}
日志通知:
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Date;
/**
* 日志切面类
* 切面 = 通知+切入点
*/
@Aspect // 标记为切面类
@Component // 把切面对象放入IOC容器中
public class LogAspect {
// 注入数据
@Autowired
private HttpServletRequest request;
@Autowired
private SysLogService sysLogService;
@Autowired
private HttpSession session;
/**
* 环绕通知方法:记录日志的方法
* ProceedingJoinPoint:控制目标对象的方法执行 或 获取目标对象的信息
*/
@Around(value = "execution(* cn.itcast.web.controller.*.*.*(..))")
public Object log(ProceedingJoinPoint jp){
// result:目标对象的方法返回值
Object result = null;
// 1.获取当前登陆用户
User loginUser = (User) session.getAttribute("loginUser");
// 获取当前目标执行的方法
// 2.getSignature:获取目标方法信息
String methodName = jp.getSignature().getName();
// 3.getTarget():获取目标对象
String fullClassName = jp.getTarget().getClass().getName();
try{
// 创建对象
SysLog sysLog = new SysLog();
// 4 封装日志信息
// 4.1 封装操作用户的信息
if (loginUser != null){
sysLog.setUserName(loginUser.getUserName());
sysLog.setCompanyId(loginUser.getManagerId());
sysLog.setCompanyName(loginUser.getCompanyName());
}
// 4.2 获取当前用的IP地址
String ip = request.getLocalAddr();
sysLog.setIp(ip);
// 4.3 设置操作时间
sysLog.setTime(new Date());
// 4.4 设置方法名称
sysLog.setMethod(methodName);
// 4.5 设置操作的那个类
sysLog.setAction(fullClassName);
// 编写前置通知代码
// 5.保存日志信息
sysLogService.save(sysLog);
// 编写后置通知代码
// 6.执行目标对象的方法
result = jp.proceed();
}catch (Throwable throwable){
// 异常通知代码
throwable.printStackTrace();
}finally {
// 编写最终通知代码
}
return result;
}
}
task消息队列,定时发送邮件:
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* 每天早上10点,对交期还有2天的购销合同,发送提醒邮件
*/
public class ContractTask {
// 通过本地方式 来注入 远程服务
@Autowired
@Qualifier("contractService") // Qualifier:指定bean的id进行注入
private ContractService contractService;
//注入RabbitTemplate对象
@Autowired
private RabbitTemplate rabbitTemplate;
public void execute(){
// System.out.println("触发了任务...");
// 1.调用业务,查询还有2天到期的购销合同
List<ContractTaskVo> list = contractService.findByPeriod(2);
// 2.遍历,给每个合同创建人发送提醒邮件
if (list != null && list.size() > 0){
for (ContractTaskVo contractTaskVo :list){
/* // 交货期限
Date deliveryPeriod = contractTaskVo.getDeliveryPeriod();
String delvertyDate = new SimpleDateFormat("yyyy-MM-dd").format(deliveryPeriod);
// 收件人
String receiver = contractTaskVo.getEmail();
// 标题
String title = "交货期限到期提醒邮件";
// 内容
String content = "尊敬的"+contractTaskVo.getUserName()+":你有一个合同(合同号):"
+contractTaskVo.getContractNo()+"在"+delvertyDate+"到期,请及时处理,如果已经处理请忽略";
// 发送邮件
try {
MailUtils.sendMsg("1264385169@qq.com",title,content);
System.out.println("发送成功");
} catch (Exception e) {
e.printStackTrace();
}*/
//异步发送消息到RabbitMQ
rabbitTemplate.convertAndSend("contract.delivery",contractTaskVo);
}
}
}
}
用户认证和授权:
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
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 org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 用户的认证和授权的Realm
*/
public class AuthRealm extends AuthorizingRealm {
// 注入serssion
@Autowired
private UserService userService;
/**
* 认证方法,该方法编写认证逻辑
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//System.out.println("认证方法");
// 1.判断用户名是否存在
// 1.1 获取当前用户输入的登录数据
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
// 1.2获取登录时的用户名
String email = token.getUsername();
//1.3查询数据库判断邮箱是否存在
User loginUser = userService.findByEmail(email);
// 1.4 判断用户是否存在
if (loginUser == null){
// 用户不存在
return null; // 只需要返回null,Shiro自动抛出UnknownAccountException异常
}
//2.返回数据库的密码,让Shiro判断密码是否正确
/**
* 参数一:用户登录成功后,需要获取的登录数据(使用Subject.getPrincipal获取的)
* 参数二:(最重要)数据库的密码
* 参数三:只有在多个Realm(多张用户表)的情况下才有用的(如果只有一个Realm可以随便写)
*/
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(loginUser,loginUser.getPassword(),loginUser.getUserName());
return info;
}
/**
* 授权方法,该方法编写授权逻辑
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// System.out.println("授权方法");
// 1.创建SimpleAuthorizationInfo授权对象
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 2.根据当前登录用户查询分配的权限(模块)
// 2.1 获取登录用户的信息
Subject subject = SecurityUtils.getSubject();
User loginUser = (User) subject.getPrincipal();
// 2.2 查询用户分配的权限
List<Module> moduleList = userService.findModulesByUserId(loginUser.getId());
// 3. 把模块的权限标记(cpermission字段值)存入授权对象
Set<String> permissions = new HashSet<>();
// 3.1 遍历所有模块数据
if(moduleList!=null && moduleList.size()>0){
for(Module module:moduleList){
//3.2 存入模块的权限标记(cpermission字段值)
if(module.getCpermission()!=null){
permissions.add(module.getCpermission());
}
}
}
// 3.3 把所有权限标记的Set集合存入info对象中
info.addStringPermissions(permissions);
// 4.返回授权对象
return info;
}
}
密码加盐加密:
package cn.itcast.web.shiro;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.crypto.hash.Md5Hash;
/**
* 自定义凭证匹配器(加盐加密)
*/
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher {
/**
* 编写凭证匹配逻辑
* @param token 用户输入的登录信息
* @param info Realm返回的封装数据库的信息
* @return true: 密码一致
* false 密码不一致
*/
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
// 1.从token获取用户输入用户名(盐)和(密码(源密码)
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
String email = userToken.getUsername(); // 用户名(盐)
char[] password = userToken.getPassword(); // 密码
// 2.使用Md5Hash对源密码进行加盐加密,得到新密码
Md5Hash md5Hash = new Md5Hash(password,email);
String encodePwd = md5Hash.toString();
// 3.从Info取出数据库的密码(加密的)
//getCredentials: 取出AuthRealm的SimpleAuthenticationInfo对象的第二个参数的内容
Object dbPwd = info.getCredentials();
// 4.使用新密码和数据库密码比较,如果相等,返回true,否则flase;
return encodePwd.equals(dbPwd);
}
}
日期转换类型
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 日期类型转换器
* 泛型一:源类型
* 泛型二:目标类型
*/
public class StringToDateConverter implements Converter<String,Date>{
@Override
public Date convert(String s) {
Date date = null;
try {
date = new SimpleDateFormat("yyyy-MM-dd").parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
异常处理:
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 定制的全局异常处理类
*/
public class CustomExceptionRosolver implements HandlerExceptionResolver {
/**
*该方法在控制器的任何方法出现异常就会执行(注意:在控制器不能自行处理异常,不要自己try catch)
* 方法参数:
* request:请求对象
* response:响应对象
* handler:抛出异常的处理器方法(HandlerMethod)
* ex:异常对象
*/
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView mv = new ModelAndView();
//1.存入异常信息
mv.addObject("errorMsg","对不起,我错误了:"+ex.getMessage());
//2.跳转到错误提示页面
mv.setViewName("error");
return mv;
}
}
2、service远程调用:
sevice层配置dubbo注册地址applicationContext-dubbo.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:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--dubbo服务提供方-->
<!--1.dubbo的服务名称-->
<dubbo:application name="export_cargo_service"/>
<!--2.注册中心的地址配置-->
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<!--3.dubbo协议-->
<dubbo:protocol name="dubbo" port="20882"/>
<!--4.扫描dubbo注解-->
<dubbo:annotation package="cn.1111.service"/>
</beans>
tomcat启动配置web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!--加载dubbo配置-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/applicationContext-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
基于main方法的启动配置:
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
/**
* 基于main方法启动提供者
*/
public class ContractProvider {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath*:spring/applicationContext-*.xml");
context.start();
System.in.read();
}
}
3、rabbitMQ消息队列
applicationContext-rabbitmq-consumer.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:rabbit="http://www.springframework.org/schema/rabbit"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--1.创建连接工厂-->
<rabbit:connection-factory
id="connectionFactory"
host="127.0.0.1"
port="5672"
virtual-host="/heima"
username="heima"
password="123"
/>
<!--2.配置admin:管理连接与频道-->
<rabbit:admin connection-factory="connectionFactory"/>
<!-- 3.扫描监听程序,创建对象-->
<context:component-scan base-package="cn.itcast.listener"/>
<!-- 4.配置消息监听容器-->
<rabbit:listener-container connection-factory="connectionFactory">
<!--配置监听程序-->
<!--
ref:引用监听程序
queue-names: 需要监听的队列名称列表
-->
<rabbit:listener ref="contractEmailListener" queue-names="contractQueue"/>
</rabbit:listener-container>
</beans>
登录发送邮件工具类:
import javax.mail.Address;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
/**
* 电子邮件工具类
*
* @author Administrator
*/
public class MailUtil {
//实现邮件发送的方法
public static void sendMsg(String to, String subject, String content) throws Exception {
Properties props = new Properties();
props.setProperty("mail.smtp.host", "smtp.sina.com"); //设置主机地址 smtp.qq.com smtp.sina.com
props.setProperty("mail.smtp.auth", "true");//认证
//2.产生一个用于邮件发送的Session对象
Session session = Session.getInstance(props);
//3.产生一个邮件的消息对象
MimeMessage message = new MimeMessage(session);
//4.设置消息的发送者
Address fromAddr = new InternetAddress("itcast004@sina.com");
message.setFrom(fromAddr);
//5.设置消息的接收者
Address toAddr = new InternetAddress(to);
//TO 直接发送 CC抄送 BCC密送
message.setRecipient(MimeMessage.RecipientType.TO, toAddr);
//6.设置主题
message.setSubject(subject);
//7.设置正文
message.setText(content);
//8.准备发送,得到火箭
Transport transport = session.getTransport("smtp");
//9.设置火箭的发射目标
transport.connect("smtp.sina.com", "itcast004@sina.com", "loveyou");
//10.发送
transport.sendMessage(message, message.getAllRecipients());
//11.关闭
transport.close();
}
}
监听到期内容:
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 内容到期消息监听器
*/
@Component
public class ContractEmailListener implements MessageListener {
private ObjectMapper mapper = new ObjectMapper();
@Override
public void onMessage(Message message) {
//1.获取消息内容
byte[] body = message.getBody();
//2.转换为JavaBean对象
try {
ContractTaskVo contractTaskVo = mapper.readValue(body, ContractTaskVo.class);
//3.发送邮件
Date deliveryPeriod = contractTaskVo.getDeliveryPeriod();
String delvertyDate = new SimpleDateFormat("yyyy-MM-dd").format(deliveryPeriod);
//收件人
String receiver = contractTaskVo.getEmail();
//标题
String title = "交货期限到期提醒邮件";
//内容
String content = "尊敬的" + contractTaskVo.getUserName() + ":你有一个合同(合同号:" + contractTaskVo.getContractNo() + "在" + delvertyDate + "到期,请请及时处理。如果已经处理请忽略。)";
//发送邮件
try {
MailUtil.sendMsg("1264385169@qq.com", title, content);
System.out.println("发送成功");
} catch (Exception e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
4、门户入口:
web.xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!--1.启动springmvc,springmvc.xml配置dubbo-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring/springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!--2.springmvc字符集编码过滤器-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
springmvc.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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--1.springmvc配置,扫描Contexter-->
<context:component-scan base-package="cn.itcast.web"/>
<!--2.mvc注解的驱动-->
<mvc:annotation-driven/>
<!--3.dubbo服务消费配置-->
<!--3.1 dubbo服务名称-->
<dubbo:application name="export_web_portal"/>
<!--3.2 zookeeper注册地址-->
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<!--3.3 扫描@Reference注解所在包-->
<dubbo:annotation package="cn.111.web"/>
</beans>
5、父项目jar配置信息:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.itcast</groupId>
<artifactId>export_parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!--聚合项目-->
<modules>
<module>export_domain</module>
<module>export_dao</module>
<module>export_system_service</module>
<module>export_web_manager</module>
<module>export_company_interface</module>
<module>export_company_service</module>
<module>export_web_portal</module>
<module>export_cargo_service</module>
<module>export_cargo_interface</module>
<module>export_stat_interface</module>
<module>export_stat_service</module>
<module>export_mq_consumer</module>
</modules>
<!-- 集中定义依赖版本号 -->
<properties>
<junit.version>4.12</junit.version>
<spring.version>5.0.2.RELEASE</spring.version>
<pagehelper.version>5.1.8</pagehelper.version>
<servlet-api.version>2.5</servlet-api.version>
<dubbo.version>2.8.4</dubbo.version>
<zookeeper.version>3.4.7</zookeeper.version>
<zkclient.version>0.1</zkclient.version>
<mybatis.version>3.4.5</mybatis.version>
<mybatis.spring.version>1.3.1</mybatis.spring.version>
<mybatis.paginator.version>1.2.15</mybatis.paginator.version>
<mysql.version>5.1.32</mysql.version>
<druid.version>1.0.9</druid.version>
<commons-fileupload.version>1.3.1</commons-fileupload.version>
<jackson.version>2.9.5</jackson.version>
</properties>
<!--
dependencies vs dependencyManagement的区别?
1)在父工程使用dependencies,在子工程不需要导入依赖啦
2)在父工程使用dependencyManagement,在在工程还是需要导入依赖,只是不写版本号而已啦
-->
<dependencies>
<!-- Spring 依赖包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Dubbo依赖包 -->
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.11.0.GA</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
<!--web基础包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Mybatis 依赖包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis.spring.version}</version>
</dependency>
<!-- mybatis分页插件 -->
<dependency>
<groupId>com.github.miemiedev</groupId>
<artifactId>mybatis-paginator</artifactId>
<version>${mybatis.paginator.version}</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>${pagehelper.version}</version>
</dependency>
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- 文件上传组件 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons-fileupload.version}</version>
</dependency>
<!-- spring整合rabbitmq -->
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
<!-- xml解析器通用包:CXF WebService框架 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.4</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<version>1.4.01</version>
</dependency>
<!--shiro-->
<!--shiro和spring整合-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!--shiro核心包-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<!--测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- jackson支持包 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<!--日志包-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--JavaMail支持包-->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4</version>
</dependency>
<!--定时任务 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.3</version>
</dependency>
</dependencies>
</project>