SSM(Spring、SpringMVC、Mybatis)实现登录操作(配置文件、验证码登录、错误页面跳转、log4j2 配置、拦截器配置)

1、项目目录

SSM(Spring、SpringMVC、Mybatis)实现登录操作(配置文件、验证码登录、错误页面跳转、log4j2 配置、拦截器配置)_第1张图片

二、配置文件

1、pom.xml



<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.0modelVersion>

    <groupId>cn.lemon.demogroupId>
    <artifactId>demoartifactId>
    <version>1.0-SNAPSHOTversion>
    <packaging>warpackaging>

    <name>base Maven Webappname>
    
    <url>http://www.example.comurl>

    <properties>
        
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
        <java.version>1.8java.version>
    properties>

    <dependencies>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-webmvcartifactId>
            <version>5.1.5.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>5.1.5.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.aspectjgroupId>
            <artifactId>aspectjweaverartifactId>
            <version>1.8.13version>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-txartifactId>
            <version>5.1.5.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-jdbcartifactId>
            <version>5.1.5.RELEASEversion>
        dependency>

        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-testartifactId>
            <version>5.1.5.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.mybatisgroupId>
            <artifactId>mybatisartifactId>
            <version>3.4.6version>
        dependency>
        <dependency>
            <groupId>org.mybatisgroupId>
            <artifactId>mybatis-springartifactId>
            <version>1.3.2version>
        dependency>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.46version>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.16.20version>
        dependency>
        <dependency>
            <groupId>org.apache.commonsgroupId>
            <artifactId>commons-lang3artifactId>
            <version>3.0version>
        dependency>
        <dependency>
            <groupId>com.github.pagehelpergroupId>
            <artifactId>pagehelperartifactId>
            <version>5.1.4version>
        dependency>
        <dependency>
            <groupId>com.mchangegroupId>
            <artifactId>c3p0artifactId>
            <version>0.9.5.5version>
        dependency>
        <dependency>
            <groupId>commons-fileuploadgroupId>
            <artifactId>commons-fileuploadartifactId>
            <version>1.3.3version>
        dependency>
        <dependency>
            <groupId>jstlgroupId>
            <artifactId>jstlartifactId>
            <version>1.2version>
        dependency>
        <dependency>
            <groupId>net.sf.json-libgroupId>
            <artifactId>json-libartifactId>
            <version>2.2.3version>
            
            <classifier>jdk15classifier>
        dependency>
        <dependency>
            <groupId>javax.servletgroupId>
            <artifactId>javax.servlet-apiartifactId>
            <version>3.1.0version>
            <scope>providedscope>
        dependency>
        <dependency>
            <groupId>javax.servlet.jspgroupId>
            <artifactId>javax.servlet.jsp-apiartifactId>
            <version>2.3.0version>
        dependency>
        <dependency>
            <groupId>org.slf4jgroupId>
            <artifactId>slf4j-apiartifactId>
            <version>1.7.25version>
        dependency>
        <dependency>
            <groupId>org.slf4jgroupId>
            <artifactId>slf4j-log4j12artifactId>
            <version>1.7.10version>
        dependency>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
            <scope>testscope>
        dependency>
        <dependency>
            <groupId>org.apache.logging.log4jgroupId>
            <artifactId>log4j-apiartifactId>
            <version>2.10.0version>
        dependency>
        <dependency>
            <groupId>org.apache.logging.log4jgroupId>
            <artifactId>log4j-coreartifactId>
            <version>2.10.0version>
        dependency>

        <dependency>
            <groupId>org.apache.logging.log4jgroupId>
            <artifactId>log4j-webartifactId>
            <version>2.9.1version>
        dependency>
        <dependency>
            <groupId>com.google.code.gsongroupId>
            <artifactId>gsonartifactId>
            <version>2.8.5version>
        dependency>
    dependencies>
project>

2、入口文件:web.xml


<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    
    <filter>
        <filter-name>CharacterEncodingFilterfilter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
        <init-param>
            <param-name>encodingparam-name>
            <param-value>UTF-8param-value>
        init-param>

        <init-param>
            <param-name>forceEncodingparam-name>
            <param-value>trueparam-value>
        init-param>
    filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilterfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>

    
    <filter>
        <filter-name>HiddenHttpMethodFilterfilter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilterfilter-class>
    filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilterfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>

    
    <context-param>
        <param-name>contextConfigLocationparam-name>
        <param-value>classpath:spring/applicationContext.xmlparam-value>
    context-param>

    
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
    listener>

    
    <context-param>
        <param-name>log4jConfigLocationparam-name>
        <param-value>classpath:log4j2.xmlparam-value>
    context-param>
    <listener>
        <listener-class>org.apache.logging.log4j.web.Log4jServletContextListenerlistener-class>
    listener>

    
    <servlet>
        <servlet-name>DispatcherServletservlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
        <init-param>
            <param-name>contextConfigLocationparam-name>
            <param-value>classpath:/springmvc/springmvc.xmlparam-value>
        init-param>
        <load-on-startup>1load-on-startup>
    servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServletservlet-name>
        <url-pattern>/url-pattern>
    servlet-mapping>

    
    <error-page>
        <error-code>404error-code>
        <location>/WEB-INF/errors/404.jsplocation>
    error-page>

    <error-page>
        <error-code>500error-code>
        <location>/WEB-INF/errors/500.jsplocation>
    error-page>

    
    <welcome-file-list>
        <welcome-file>index.jspwelcome-file>
    welcome-file-list>
web-app>

3、Spring 配置文件:applicationContext.xml


<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:tx="http://www.springframework.org/schema/tx"
	   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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

	
	<context:component-scan base-package="cn.lemon.demo">
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/>
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
	context:component-scan>

	
	<context:property-placeholder location="classpath:db.properties"/>

	
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
		<property name="driverClass" value="${datasource.connection.driver_class}"/>
		<property name="jdbcUrl" value="${datasource.connection.url}"/>
		<property name="user" value="${datasource.connection.username}"/>
		<property name="password" value="${datasource.connection.password}"/>
		<property name="minPoolSize" value="${datasource.connection.minPoolSize}"/>
		
		<property name="maxPoolSize" value="${datasource.connection.maxPoolSize}"/>
		
		<property name="maxIdleTime" value="${datasource.connection.maxIdleTime}"/>
		
		<property name="acquireIncrement" value="${datasource.connection.acquireIncrement}"/>
		
		<property name="maxStatements" value="${datasource.connection.maxStatements}"/>
		
		<property name="maxStatementsPerConnection" value="${datasource.connection.maxStatementsPerConnection}"/>
		
		<property name="initialPoolSize" value="${datasource.connection.initialPoolSize}"/>
		
		<property name="idleConnectionTestPeriod" value="${datasource.connection.idleConnectionTestPeriod}"/>
		
		<property name="acquireRetryAttempts" value="${datasource.connection.acquireRetryAttempts}"/>
		
		<property name="breakAfterAcquireFailure" value="${datasource.connection.breakAfterAcquireFailure}"/>
		
		<property name="testConnectionOnCheckout" value="${datasource.connection.testConnectionOnCheckout}"/>
		<property name="checkoutTimeout" value="${datasource.connection.checkoutTimeout}"/>
		<property name="testConnectionOnCheckin" value="${datasource.connection.testConnectionOnCheckin}"/>
		<property name="automaticTestTable" value="${datasource.connection.automaticTestTable}"/>
		<property name="acquireRetryDelay" value="${datasource.connection.acquireRetryDelay}"/>
		
		<property name="unreturnedConnectionTimeout" value="${datasource.connection.unreturnedConnectionTimeout}"/>
		
		<property name="maxIdleTimeExcessConnections" value="${datasource.connection.maxIdleTimeExcessConnections}"/>
		<property name="maxConnectionAge" value="${datasource.connection.maxConnectionAge}"/>
	bean>

	
	<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource">property>
	bean>

	
	<tx:annotation-driven transaction-manager="dataSourceTransactionManager" proxy-target-class="true">tx:annotation-driven>

	
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"/>
		<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
		<property name="mapperLocations" value="classpath:mybatis/*/*.xml"/>
		
		
	bean>

	
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="cn.lemon.demo.dao"/>
		
		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
	bean>
	
	<bean id="gson" class="com.google.gson.Gson" scope="prototype">bean>
beans>

4、SpringMVC 配置文件:springmvc.xml


<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:task="http://www.springframework.org/schema/task"
       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/task http://www.springframework.org/schema/task/spring-task.xsd">

    
    <context:component-scan base-package="cn.lemon.demo.controller">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
    context:component-scan>

    
    <mvc:annotation-driven>mvc:annotation-driven>

    
    <task:annotation-driven/>

    
    <mvc:default-servlet-handler/>

    
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/">property>
        <property name="suffix" value=".jsp">property>
    bean>

    
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        
        <property name="maxUploadSize">
            <value>10485760value>
        property>
        
        <property name="defaultEncoding">
            <value>UTF-8value>
        property>
    bean>

    
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/admin/**"/>
            <mvc:mapping path="/system/*"/>
            <mvc:exclude-mapping path="/system/login"/>
            <mvc:exclude-mapping path="/system/get_cpacha"/>
            <mvc:exclude-mapping path="/static/**"/>
            <bean class="cn.lemon.demo.interceptor.LoginInterceptor">bean>
        mvc:interceptor>
    mvc:interceptors>
beans>

5、Mybatis 配置文件:mybatis-config.xml



<configuration>
	<settings>
		<setting name="lazyLoadingEnabled" value="false"/>
		<setting name="aggressiveLazyLoading" value="false"/>
		<setting name="cacheEnabled" value="true"/>
	settings>

	
	<typeAliases>
		
		
		
		<package name="cn.lemon.demo.domain">package>
	typeAliases>
configuration>

6、数据库配置文件:db.properties

datasource.connection.driver_class=com.mysql.jdbc.Driver
datasource.connection.url=jdbc:mysql://localhost:3306/db_base_ssm?useUnicode=true&characterEncoding=utf-8
datasource.connection.username=root
datasource.connection.password=root
#连接池保持的最小连接数,default : 3(建议使用)
datasource.connection.minPoolSize=3
#连接池中拥有的最大连接数,如果获得新连接时会使连接总数超过这个值则不会再获取新连接,而是等待其他连接释放,所以这个值有可能会设计地很大,default : 15(建议使用)
datasource.connection.maxPoolSize=15
#连接的最大空闲时间,如果超过这个时间,某个数据库连接还没有被使用,则会断开掉这个连接。如果为0,则永远不会断开连接,即回收此连接。default : 0 单位 s(建议使用)
datasource.connection.maxIdleTime=0
#连接池在无空闲连接可用时一次性创建的新数据库连接数,default : 3(建议使用)
datasource.connection.acquireIncrement=3
#连接池为数据源缓存的PreparedStatement的总数。由于PreparedStatement属于单个Connection,所以这个数量应该根据应用中平均连接数乘以每个连接的平均PreparedStatement来计算。同时maxStatementsPerConnection的配置无效。default : 0(不建议使用)
datasource.connection.maxStatements=0
#连接池为数据源单个Connection缓存的PreparedStatement数,这个配置比maxStatements更有意义,因为它缓存的服务对象是单个数据连接,如果设置的好,肯定是可以提高性能的。为0的时候不缓存。default : 0(看情况而论)
datasource.connection.maxStatementsPerConnection=0
#连接池初始化时创建的连接数,default : 3(建议使用)
datasource.connection.initialPoolSize=3
#用来配置测试空闲连接的间隔时间。测试方式还是上面的两种之一,可以用来解决MySQL8小时断开连接的问题。因为它保证连接池会每隔一定时间对空闲连接进行一次测试,从而保证有效的空闲连接能每隔一定时间访问一次数据库,将于MySQL8小时无会话的状态打破。为0则不测试。default : 0(建议使用)
datasource.connection.idleConnectionTestPeriod=0
#连接池在获得新连接失败时重试的次数,如果小于等于0则无限重试直至连接获得成功。default : 30(建议使用)
datasource.connection.acquireRetryAttempts=30
#如果为true,则当连接获取失败时自动关闭数据源,除非重新启动应用程序。所以一般不用。default : false(不建议使用)
datasource.connection.breakAfterAcquireFailure=false
#性能消耗大。如果为true,在每次getConnection的时候都会测试,为了提高性能,尽量不要用。default : false(不建议使用)
datasource.connection.testConnectionOnCheckout=false
#配置当连接池所有连接用完时应用程序getConnection的等待时间。为0则无限等待直至有其他连接释放或者创建新的连接,不为0则当时间到的时候如果仍没有获得连接,则会抛出SQLException。其实就是acquireRetryAttempts*acquireRetryDelay。default : 0(与上面两个,有重复,选择其中两个都行)
datasource.connection.checkoutTimeout=30000
#如果为true,则在close的时候测试连接的有效性。default : false(不建议使用)
datasource.connection.testConnectionOnCheckin=false
#配置一个表名,连接池根据这个表名用自己的测试sql语句在这个空表上测试数据库连接,这个表只能由c3p0来使用,用户不能操作。default : null(不建议使用)
datasource.connection.automaticTestTable=c3p0TestTable
#连接池在获得新连接时的间隔时间。default : 1000 单位ms(建议使用)
datasource.connection.acquireRetryDelay=1000
#为0的时候要求所有的Connection在应用程序中必须关闭。如果不为0,则强制在设定的时间到达后回收Connection,所以必须小心设置,保证在回收之前所有数据库操作都能够完成。这种限制减少Connection未关闭情况的不是很适用。建议手动关闭。default : 0 单位 s(不建议使用)
datasource.connection.unreturnedConnectionTimeout=0
#这个配置主要是为了快速减轻连接池的负载,比如连接池中连接数因为某次数据访问高峰导致创建了很多数据连接,但是后面的时间段需要的数据库连接数很少,需要快速释放,必须小于maxIdleTime。其实这个没必要配置,maxIdleTime已经配置了。default : 0 单位 s(不建议使用)
datasource.connection.maxIdleTimeExcessConnections=0
#配置连接的生存时间,超过这个时间的连接将由连接池自动断开丢弃掉。当然正在使用的连接不会马上断开,而是等待它close再断开。配置为0的时候则不会对连接的生存时间进行限制。default : 0 单位 s(不建议使用)
datasource.connection.maxConnectionAge=0

7、日志配置文件:log4j2.xml



<Configuration status="INFO">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n"/>
        Console>

        <RollingFile name="RollingFile" filename="log/test.log"
                     filepattern="${logPath}/%d{YYYYMMddHHmmss}-fargo.log">
            <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c{1}:%L - %msg%n"/>
            <Policies>
                <SizeBasedTriggeringPolicy size="10 MB"/>
            Policies>
            <DefaultRolloverStrategy max="20"/>
        RollingFile>
    Appenders>

    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
            <AppenderRef ref="RollingFile"/>
        Root>
    Loggers>
Configuration>

三、实体类、数据访问层、业务逻辑层、控制层

1、实体类:User.java

package cn.lemon.demo.domain;

import lombok.Data;
import org.springframework.stereotype.Component;

@Component
@Data
public class User {
    private Integer id;
    private String username;
    private String password;
    private String photo;
    private int sex;//0:未知; 1:男; 2:女;
    private int age;
    private String address;
}

2、数据访问层:IUserDao.java,映射文件:IUserDao.xml

package cn.lemon.demo.dao;

import cn.lemon.demo.domain.User;
import org.springframework.stereotype.Repository;

@Repository
public interface IUserDao {
    User findByUsername(String username);
}


<mapper namespace="cn.lemon.demo.dao.IUserDao">
    <select id="findByUsername" resultType="User">
        select * from user where username = #{username}
    select>
mapper>

3、业务逻辑层:IUserService.javaUserServiceImpl.xml

package cn.lemon.demo.service;

import cn.lemon.demo.domain.User;
import org.springframework.stereotype.Service;

@Service
public interface IUserService {
    User findByUsername(String username);
}
package cn.lemon.demo.service.impl;

import cn.lemon.demo.dao.IUserDao;
import cn.lemon.demo.domain.User;
import cn.lemon.demo.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements IUserService {
    @Autowired
    private IUserDao iUserDao;

    @Override
    public User findByUsername(String username) {
        return iUserDao.findByUsername(username);
    }
}

4、控制层:SystemController.java

package cn.lemon.demo.controller;

import cn.lemon.demo.domain.User;
import cn.lemon.demo.service.impl.UserServiceImpl;
import cn.lemon.demo.utils.CpachaUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/system")
public class SystemController {
    @Autowired
    private UserServiceImpl userService;

    //登录页面
    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public ModelAndView login(ModelAndView model) {
        model.setViewName("system/login");
        return model;
    }

    //系统登录后的主页
    @RequestMapping(value = "/index", method = RequestMethod.GET)
    public ModelAndView index(ModelAndView model) {
        model.setViewName("system/index");
        return model;
    }

    //登录表单提交处理控制器
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, String> loginAct(User user, String cpacha, HttpServletRequest request) {
        Map<String, String> ret = new HashMap<>();
        if (user == null) {
            ret.put("type", "error");
            ret.put("msg", "请填写用户信息!!");
            return ret;
        }
        if (StringUtils.isEmpty(cpacha)) {
            ret.put("type", "error");
            ret.put("msg", "请填写验证码!!");
            return ret;
        }
        if (StringUtils.isEmpty(user.getUsername())) {
            ret.put("type", "error");
            ret.put("msg", "请填写用户名!!");
            return ret;
        }
        if (StringUtils.isEmpty(user.getPassword())) {
            ret.put("type", "error");
            ret.put("msg", "请填写密码!!");
            return ret;
        }
        Object loginCpacha = request.getSession().getAttribute("loginCpacha");
        if (loginCpacha == null) {
            ret.put("type", "error");
            ret.put("msg", "会话超时,请刷新页面!!");
            return ret;
        }
        if (!cpacha.toUpperCase().equals(loginCpacha.toString().toUpperCase())) {
            ret.put("type", "error");
            ret.put("msg", "验证码错误!");
            return ret;
        }
        User findByUsername = userService.findByUsername(user.getUsername());
        if (findByUsername == null) {
            ret.put("type", "error");
            ret.put("msg", "用户名不存在!");
            return ret;
        }
        if (!user.getPassword().equals(findByUsername.getPassword())) {
            ret.put("type", "error");
            ret.put("msg", "密码错误!");
            return ret;
        }
        //把登录的用户写进 session
        request.getSession().setAttribute("admin", findByUsername);
        ret.put("type", "success");
        ret.put("msg", "登录成功!");
        return ret;
    }

    //本系统所有的验证码均采用此方法
    @RequestMapping(value = "/get_cpacha", method = RequestMethod.GET)
    public void generateCpacha(
            @RequestParam(name = "vl", required = false, defaultValue = "4") Integer vcodeLen,
            @RequestParam(name = "w", required = false, defaultValue = "110") Integer width,
            @RequestParam(name = "h", required = false, defaultValue = "30") Integer height,
            @RequestParam(name = "type", required = true, defaultValue = "loginCpacha") String cpachaType,
            HttpServletRequest request,
            HttpServletResponse response) {
        CpachaUtil cpachaUtil = new CpachaUtil(vcodeLen, width, height);
        String generatorVCode = cpachaUtil.generatorVCode();
        //把验证码写入 Session
        request.getSession().setAttribute(cpachaType, generatorVCode);
        BufferedImage generatorRotateVCodeImage = cpachaUtil.generatorRotateVCodeImage(generatorVCode, true);
        try {
            ImageIO.write(generatorRotateVCodeImage, "gif", response.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

四、验证码工具类

CpachaUtil.java

package cn.lemon.demo.utils;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.Random;

/**
 * 验证码生成器
 *
 * @author llq
 */
public class CpachaUtil {

    /**
     * 验证码来源
     */
    final private char[] code = {
            '2', '3', '4', '5', '6', '7', '8', '9',
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
            'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v',
            'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F',
            'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R',
            'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
    };
    /**
     * 字体
     */
    final private String[] fontNames = new String[]{
            "黑体", "宋体", "Courier", "Arial",
            "Verdana", "Times", "Tahoma", "Georgia"};
    /**
     * 字体样式
     */
    final private int[] fontStyles = new int[]{
            Font.BOLD, Font.ITALIC | Font.BOLD
    };

    /**
     * 验证码长度
     * 默认4个字符
     */
    private int vcodeLen = 4;
    /**
     * 验证码图片字体大小
     * 默认17
     */
    private int fontsize = 21;
    /**
     * 验证码图片宽度
     */
    private int width = (fontsize + 1) * vcodeLen + 10;
    /**
     * 验证码图片高度
     */
    private int height = fontsize + 12;
    /**
     * 干扰线条数
     * 默认3条
     */
    private int disturbline = 3;


    public CpachaUtil() {
    }

    /**
     * 指定验证码长度
     *
     * @param vcodeLen 验证码长度
     */
    public CpachaUtil(int vcodeLen) {
        this.vcodeLen = vcodeLen;
        this.width = (fontsize + 1) * vcodeLen + 10;
    }

    /**
     * 指定验证码长度、图片宽度、高度
     *
     * @param vcodeLen
     * @param width
     * @param height
     */
    public CpachaUtil(int vcodeLen, int width, int height) {
        this.vcodeLen = vcodeLen;
        this.width = width;
        this.height = height;
    }

    /**
     * 生成验证码图片
     *
     * @param vcode    要画的验证码
     * @param drawline 是否画干扰线
     * @return
     */
    public BufferedImage generatorVCodeImage(String vcode, boolean drawline) {
        //创建验证码图片
        BufferedImage vcodeImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = vcodeImage.getGraphics();
        //填充背景色
        g.setColor(new Color(246, 240, 250));
        g.fillRect(0, 0, width, height);
        if (drawline) {
            drawDisturbLine(g);
        }
        //用于生成伪随机数
        Random ran = new Random();
        //在图片上画验证码
        for (int i = 0; i < vcode.length(); i++) {
            //设置字体
            g.setFont(new Font(fontNames[ran.nextInt(fontNames.length)], fontStyles[ran.nextInt(fontStyles.length)], fontsize));
            //随机生成颜色
            g.setColor(getRandomColor());
            //画验证码
            g.drawString(vcode.charAt(i) + "", i * fontsize + 10, fontsize + 5);
        }
        //释放此图形的上下文以及它使用的所有系统资源
        g.dispose();

        return vcodeImage;
    }

    /**
     * 获得旋转字体的验证码图片
     *
     * @param vcode
     * @param drawline 是否画干扰线
     * @return
     */
    public BufferedImage generatorRotateVCodeImage(String vcode, boolean drawline) {
        //创建验证码图片
        BufferedImage rotateVcodeImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = rotateVcodeImage.createGraphics();
        //填充背景色
        g2d.setColor(new Color(246, 240, 250));
        g2d.fillRect(0, 0, width, height);
        if (drawline) {
            drawDisturbLine(g2d);
        }
        //在图片上画验证码
        for (int i = 0; i < vcode.length(); i++) {
            BufferedImage rotateImage = getRotateImage(vcode.charAt(i));
            g2d.drawImage(rotateImage, null, (int) (this.height * 0.7) * i, 0);
        }
        g2d.dispose();
        return rotateVcodeImage;
    }

    /**
     * 生成验证码
     *
     * @return 验证码
     */
    public String generatorVCode() {
        int len = code.length;
        Random ran = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < vcodeLen; i++) {
            int index = ran.nextInt(len);
            sb.append(code[index]);
        }
        return sb.toString();
    }

    /**
     * 为验证码图片画一些干扰线
     *
     * @param g
     */
    private void drawDisturbLine(Graphics g) {
        Random ran = new Random();
        for (int i = 0; i < disturbline; i++) {
            int x1 = ran.nextInt(width);
            int y1 = ran.nextInt(height);
            int x2 = ran.nextInt(width);
            int y2 = ran.nextInt(height);
            g.setColor(getRandomColor());
            //画干扰线
            g.drawLine(x1, y1, x2, y2);
        }
    }

    /**
     * 获取一张旋转的图片
     *
     * @param c 要画的字符
     * @return
     */
    private BufferedImage getRotateImage(char c) {
        BufferedImage rotateImage = new BufferedImage(height, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = rotateImage.createGraphics();
        //设置透明度为0
        g2d.setColor(new Color(255, 255, 255, 0));
        g2d.fillRect(0, 0, height, height);
        Random ran = new Random();
        g2d.setFont(new Font(fontNames[ran.nextInt(fontNames.length)], fontStyles[ran.nextInt(fontStyles.length)], fontsize));
        g2d.setColor(getRandomColor());
        double theta = getTheta();
        //旋转图片
        g2d.rotate(theta, height / 2, height / 2);
        g2d.drawString(Character.toString(c), (height - fontsize) / 2, fontsize + 5);
        g2d.dispose();

        return rotateImage;
    }

    /**
     * @return 返回一个随机颜色
     */
    private Color getRandomColor() {
        Random ran = new Random();
        return new Color(ran.nextInt(220), ran.nextInt(220), ran.nextInt(220));
    }

    /**
     * @return 角度
     */
    private double getTheta() {
        return ((int) (Math.random() * 1000) % 2 == 0 ? -1 : 1) * Math.random();
    }

    /**
     * @return 验证码字符个数
     */
    public int getVcodeLen() {
        return vcodeLen;
    }

    /**
     * 设置验证码字符个数
     *
     * @param vcodeLen
     */
    public void setVcodeLen(int vcodeLen) {
        this.width = (fontsize + 3) * vcodeLen + 10;
        this.vcodeLen = vcodeLen;
    }

    /**
     * @return 字体大小
     */
    public int getFontsize() {
        return fontsize;
    }

    /**
     * 设置字体大小
     *
     * @param fontsize
     */
    public void setFontsize(int fontsize) {
        this.width = (fontsize + 3) * vcodeLen + 10;
        this.height = fontsize + 15;
        this.fontsize = fontsize;
    }

    /**
     * @return 图片宽度
     */
    public int getWidth() {
        return width;
    }

    /**
     * 设置图片宽度
     *
     * @param width
     */
    public void setWidth(int width) {
        this.width = width;
    }

    /**
     * @return 图片高度
     */
    public int getHeight() {
        return height;
    }

    /**
     * 设置图片高度
     *
     * @param height
     */
    public void setHeight(int height) {
        this.height = height;
    }

    /**
     * @return 干扰线条数
     */
    public int getDisturbline() {
        return disturbline;
    }

    /**
     * 设置干扰线条数
     *
     * @param disturbline
     */
    public void setDisturbline(int disturbline) {
        this.disturbline = disturbline;
    }
}

五、登录拦截器

LoginInterceptor.java

package cn.lemon.demo.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONObject;
import java.util.HashMap;
import java.util.Map;

/**
 * 后台登录拦截器
 */
public class LoginInterceptor implements HandlerInterceptor {
    /**
     * 在请求之前的动作,对于登录来说,需要在请求之前做拦截
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        /**
         * 下面三行可以测试,当访问一个路径时,会被拦截,并在控制台打印 :链接。。。。进入拦截器
         * return false :表示拦截
         */
        //String requestURI = request.getRequestURI();
        //System.out.println("链接" + requestURI + "进入拦截器");
        //return false;//表示拦截,return true; 表示可以进入,也就是不拦截
        String requestURI = request.getRequestURI();
        Object admin = request.getSession().getAttribute("admin");
        if (admin == null){
            //表示未登陆或者登陆失败
            System.out.println("链接" + requestURI + "进入拦截器");
            String header = request.getHeader("X-Requested-With");
            //判断是否是 Ajax 请求
            if ("XMLHttpRequest".equals(handler)){
                Map<String,String> ret = new HashMap<>();
                ret.put("type","error");
                ret.put("msg","登陆会话超时或还未登陆,请重新登陆!");
                response.getWriter().write(JSONObject.fromObject(ret).toString());
                return false;
            }
            //表示普通链接跳转,直接重定向到登陆页面
            response.sendRedirect(request.getServletContext().getContextPath() + "/system/login");
            return false;
        }
        return true;
    }

    /**
     * 在请求的时候的动作
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    /**
     * 在请求之后的动作
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

六、jsp页面文件

1、入口文件:index.jap

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--使用这个,需要导入 jstl.jar 和standard.jar包--%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%
    response.sendRedirect("system/login");
%>

2、登录页面:login.jap

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2020-05-24
  Time: 13:49
  To change this template use File | Settings | File Templates.
--%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>

<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>后台管理员登录title>
    <meta name="description" content="particles.js is a lightweight JavaScript library for creating particles.">
    <meta name="author" content="Vincent Garreau">
    <meta name="viewport"
          content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <link rel="stylesheet" media="screen" href="${pageContext.request.contextPath}/static/admin/login/css/style.css">
    <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath}/static/admin/login/css/reset.css">
<body>

<div id="particles-js">
    <div class="login" style="display: block;">
        <div class="login-top">
            登录
        div>
        <div class="login-center clearfix">
            <div class="login-center-img"><img
                    src="${pageContext.request.contextPath}/static/admin/login/images/name.png">
            div>
            <div class="login-center-input">
                <input type="text" name="username" id="username" value="" placeholder="请输入您的用户名"
                       onfocus="this.placeholder=''" onblur="this.placeholder='请输入您的用户名'">
                <div class="login-center-input-text">用户名div>
            div>
        div>
        <div class="login-center clearfix">
            <div class="login-center-img"><img
                    src="${pageContext.request.contextPath}/static/admin/login/images/password.png">
            div>
            <div class="login-center-input">
                <input type="password" name="password" id="password" value="" placeholder="请输入您的密码"
                       onfocus="this.placeholder=''" onblur="this.placeholder='请输入您的密码'">
                <div class="login-center-input-text">密码div>
            div>
        div>
        <div class="login-center clearfix">
            <div class="login-center-img"><img
                    src="${pageContext.request.contextPath}/static/admin/login/images/cpacha.png">
            div>
            <div class="login-center-input">
                <input style="width:50%;" type="text" name="cpacha" id="cpacha" value="" placeholder="请输入验证码"
                       onfocus="this.placeholder=''" onblur="this.placeholder='请输入验证码'">
                <div class="login-center-input-text">验证码div>
                <img id="cpacha-img" title="点击切换验证码" style="cursor:pointer;"
                     src="get_cpacha?vl=4&w=150&h=40&type=loginCpacha" width="110px" height="30px"
                     onclick="changeCpacha()">
            div>
        div>
        <div class="login-button">
            登录
        div>
    div>
    <div class="sk-rotating-plane">div>
    <canvas class="particles-js-canvas-el" width="1147" height="952" style="width: 100%; height: 100%;">canvas>
div>


<script src="${pageContext.request.contextPath}/static/admin/login/js/particles.min.js">script>
<script src="${pageContext.request.contextPath}/static/admin/login/js/app.js">script>
<script src="${pageContext.request.contextPath}/static/admin/login/js/jquery-1.8.0.min.js">script>
<script type="text/javascript">
    function hasClass(elem, cls) {
        cls = cls || '';
        if (cls.replace(/\s/g, '').length == 0) return false; //当cls没有参数时,返回false
        return new RegExp(' ' + cls + ' ').test(' ' + elem.className + ' ');
    }

    function addClass(ele, cls) {
        if (!hasClass(ele, cls)) {
            ele.className = ele.className == '' ? cls : ele.className + ' ' + cls;
        }
    }

    function removeClass(ele, cls) {
        if (hasClass(ele, cls)) {
            var newClass = ' ' + ele.className.replace(/[\t\r\n]/g, '') + ' ';
            while (newClass.indexOf(' ' + cls + ' ') >= 0) {
                newClass = newClass.replace(' ' + cls + ' ', ' ');
            }
            ele.className = newClass.replace(/^\s+|\s+$/g, '');
        }
    }

    function changeCpacha() {
        $("#cpacha-img").attr("src", 'get_cpacha?vl=4&w=150&h=40&type=loginCpacha&t=' + new Date().getTime());
    }

    document.querySelector(".login-button").onclick = function () {
        var username = $("#username").val();
        var password = $("#password").val();
        var cpacha = $("#cpacha").val();
        if (username == '' || username == 'undefined') {
            alert("请填写用户名!");
            return;
        }
        if (password == '' || password == 'undefined') {
            alert("请填写密码!");
            return;
        }
        if (cpacha == '' || cpacha == 'undefined') {
            alert("请填写验证码!");
            return;
        }
        addClass(document.querySelector(".login"), "active")
        addClass(document.querySelector(".sk-rotating-plane"), "active")
        document.querySelector(".login").style.display = "none"
        $.ajax({
            url: 'login',
            data: {username: username, password: password, cpacha: cpacha},
            type: 'post',
            dataType: 'json',
            success: function (data) {
                if (data.type == 'success') {
                    window.parent.location = 'index';
                } else {
                    removeClass(document.querySelector(".login"), "active");
                    removeClass(document.querySelector(".sk-rotating-plane"), "active");
                    document.querySelector(".login").style.display = "block";
                    alert(data.msg);
                    changeCpacha();
                }
            }
        });
    }
script>
body>
html>

3、404页面:404.jap

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2020-05-24
  Time: 12:24
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" pageEncoding="UTF-8"%>
<%--使用这个,需要导入 jstl.jar 和standard.jar包--%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>404title>
<link href="${pageContext.request.contextPath}/static/admin/h-ui/css/H-ui.min.css" rel="stylesheet" type="text/css" />
<link href="${pageContext.request.contextPath}/static/admin/h-ui/lib/Hui-iconfont/1.0.1/iconfont.css" rel="stylesheet" type="text/css" />
head>
<body>
	<div class="page-404 text-c">
		<p class="error-title"><i class="Hui-iconfont">i>404p>
		<p class="error-description">不好意思,您访问的页面不存在~p>
	div>
body>
html>

3、500页面:500.jap

<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" pageEncoding="UTF-8"%>
<%--使用这个,需要导入 jstl.jar 和standard.jar包--%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>500title>
<link href="${pageContext.request.contextPath}/static/admin/h-ui/css/H-ui.min.css" rel="stylesheet" type="text/css" />
<link href="${pageContext.request.contextPath}/static/admin/h-ui/lib/Hui-iconfont/1.0.1/iconfont.css" rel="stylesheet" type="text/css" />
head>
<body>
	<div class="page-404 text-c">
		<p class="error-title"><i class="Hui-iconfont">i>500p>
		<p class="error-description">不好意思,服务端错误啦~p>
	div>
body>
html>

七、代码下载

下载

你可能感兴趣的:(项目)