说明
本项目适用于ssm开发新手,从配置到开发全过程详解
源代码链接:https://github.com/Trista0520/SSM_-
功能介绍
教务查询系统一共有三种角色:管理员,学生和老师,不同角色登录的界面与功能都不相同
-
登录界面
-
管理员界面
管理员可以进行课程管理,学生管理,教师管理,以及账户密码的重置
-
教师显示界面
教师可以查看所授课程,给学生打分以及修改密码
-
学生显示界面 ;
学生可以查看所有课程列表,进行选课、退课操作,查看已选课程,修改密码等
正式开始项目
- 项目搭建
创建数据库,名为examination_system:
导入项目中的sql文件到IDEA,右击运行,数据库创建完毕。
新建一个maven项目:
添加web支持:
建一些基本的目录:
添加必要的依赖:
<dependencies>
<!--junit测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--log4j测试-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-maven-plugin</artifactId>
<version>1.18.12.0</version>
</dependency>
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!--shiro依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.3</version>
</dependency>
<!--spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<!--springWEB-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<!--springMVC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!--spring tx 事务处理-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<!--spring aop-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
<!--spring-jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<!--jstl-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<!--mybatis逆向工程-->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency>
<!--mybatis spring整合包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!--hibernate 数据校验器包-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
<!--c3p0链接池-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!--Mysql数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations-java5</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
注意(此步必做):
由于需要扫描java文件和resources文件下的配置文件,所以在dependencies后要加上如下配置:
<build>
<finalName>Examination_System</finalName>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
测试Tomcat能否运行:
ProjectStructure打包那里注意要把所有的依赖包导进去!!!
如果顺利显示默认界面,说明环境搭建成功~
- SSM环境配置
Mybatis配置(mybatis-config.xml):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--
MyBatis 主配置文件
-->
<configuration>
<!--为实体类设置别名-->
<typeAliases>
<package name="com.trista.pojo"/>
</typeAliases>
<!--所有的mapper.xml都需要在核心配置文件中注册-->
<mappers>
<package name="com.trista.mapper"/>
</mappers>
</configuration>
数据库配置文件 mysql.properties:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/examination_system?useUnicode=true&characterEncoding=utf-8&ServerTimezone=UTC&useSSL=false
jdbc.username=root
jdbc.password=root
Spring整合Mybatis(spring-dao.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"
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">
<!--配置整合Mybatis-->
<!--1.关联数据库文件-->
<context:property-placeholder location="classpath:mysql.properties"/>
<!--2.数据库连接池-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--配置连接池属性-->
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!--c3p0私有属性-->
<property name="maxPoolSize" value="30"/>
<property name="minPoolSize" value="10"/>
<property name="autoCommitOnClose" value="false"/>
<property name="checkoutTimeout" value="10000"/>
<property name="acquireRetryAttempts" value="2"/>
</bean>
<!--3.配置sqlSessionFactory对象-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--注入数据库连接池-->
<property name="dataSource" ref="dataSource"/>
<!--配置MyBatis全局配置文件-->
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
</bean>
<!--4.配置扫描Dao接口包,动态实现Dao接口注入到Spring容器中-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--注入sqlSessionFactory-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--给出需要扫描Dao接口包-->
<property name="basePackage" value="com.trista.mapper"/>
</bean>
</beans>
Spring整合Sercive(spring-service.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"
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">
<!--扫描service相关的bean-->
<context:component-scan base-package="com.trista.service"/>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
SpringMVC配置(spring-mvc.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"
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">
<!--配置SpringMVC-->
<!--1.开启springMVC注解驱动-->
<mvc:annotation-driven/>
<!--2.静态资源默认servlet配置-->
<mvc:default-servlet-handler/>
<!--3.配置jsp 显示viewResolver视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--4.扫描web相关的bean-->
<context:component-scan base-package="com.trista.controller"/>
</beans>
Web基本配置(Web.xml):
<?xml version="1.0" encoding="UTF-8"?>
<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">
<!--DispatcherServlet-->
<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/applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--字符编码过滤-->
<filter>
<filter-name>encodingFilter</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>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--Session过期时间-->
<session-config>
<session-timeout>15</session-timeout>
</session-config>
<!--shiro拦截器入口-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<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>
<welcome-file-list>
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
</web-app>
Spring整合Shiro(spring-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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--组件扫描器-->
<context:component-scan base-package="com.trista.realm"/>
<!--shiro过滤器bean,id要和web.xml中filter-name一致-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!--要求登录时的链接(登录页面地址),非必须的属性,系统会自动寻找web工程根目录下的/login.jsp-->
<property name="loginUrl" value="/login"/>
<property name="filterChainDefinitions">
<value>
#这里相当于ini配置文件中的[urls]
#url=拦截器[参数],拦截器
#如果用户没有该角色,然后访问该路径会报401错误
/admin/** = authc, roles[admin]
/teacher/** = authc, roles[teacher]
/student/** = authc, roles[student]
#当访问login时,不用进行认证(anon表示匿名)
/login = anon
/logout = anon
#配置静态资源可以匿名访问
/css/** = anon
/js/** = anon
/images/** = anon
/fonts/** = anon
#除了上面/login可以匿名访问,其他路径都需要登录访问
#如果没登录,就访问其他路径会跳转到/login登录
/** = authc
</value>
</property>
</bean>
<!--配置securityManager-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--配置自定义的Realm-->
<!--loginRealm使用扫描器扫描注册成功了-->
<property name="realm" ref="loginRealm"/>
</bean>
<!--生命周期-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!--启用shiro注解-->
<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>
至此所有的配置文件编写完成~
- Mybatis层编写
编写数据库对应的实体类 :
编写Dao层的 Mapper接口和接口对应的 Mapper.xml 文件:
编写Service层的接口和实现类
到此,底层需求操作编写完毕~
- 根据需求编写Controller层
对于管理者,可以对课程列表,学生列表,教师列表进行增删该查
部分代码逻辑如下:
//根据名字搜索学生
@RequestMapping(value = "/selectStudent",method = {RequestMethod.POST})
public String selectStudent(String findByName, Model model){
List<Student> studentList = null;
studentList = studentService.queryStudentByName(findByName);
for (Student student : studentList) {
College college = collegeService.queryCollegeById(student.getCollegeID());
student.setCollegeName(college.getCollegeName());
}
model.addAttribute("studentList",studentList);
return "admin/showStudent";
}
//跳转到添加学生信息页面
@RequestMapping(value = "/addStudent", method = {RequestMethod.GET})
public String addStudentUI(Model model){
List<College> collegeList = collegeService.queryAllColleges();
model.addAttribute("collegeList", collegeList);
return "admin/addStudent";
}
//添加学生信息操作
@RequestMapping(value = "/addStudent", method = {RequestMethod.POST})
public String addStudent(Student student){
studentService.addStudent(student);
return "redirect:/admin/showStudent";
}
//跳转到修改学生信息页面
@RequestMapping(value = "/editStudent",method = {RequestMethod.GET})
public String updateStudentUI(Model model,Integer id){
List<College> collegeList = collegeService.queryAllColleges();
Student student = studentService.queryStudentById(id);
model.addAttribute("collegeList", collegeList);
model.addAttribute("student",student);
return "admin/editStudent";
}
//修改学生信息
@RequestMapping(value = "/editStudent",method = {RequestMethod.POST})
public String updateStudent(Student student){
studentService.updateStudent(student);
return "redirect:/admin/showStudent";
}
//删除学生
@RequestMapping(value = "/removeStudent",method = {RequestMethod.GET})
public String deleteStudent(Integer id){
studentService.deleteStudentById(id);
return "redirect:/admin/showStudent";
}
-
前端:利用Jquery进行参数传递
前后端参数传递注意点:
① 如果传递的是一个对象,则对象的属性名必须完全一致,否则无法匹配
②前端获取后端的参数采用${studentList}的形式
③后端获取前端传递的参数变量名要与name属性的值一致,否则需要加额外的注解
④前端传递的参数都是字符串或者Json形式,如果要转化为Date格式,还需要进行手动的格式化,否则会报错 -
Ajax进行异步验证
对于表单的填写,常常不需要刷新页面就能给用户提供一些提示信息
相关js代码如下,
var userID = null;
var userName = null;
var sex = null;
var birthYear = null;
var grade = null;
var collegeID = null;
var submitBtn = null;
var resetBtn = null;
$(function(){
userID = $("#userID");
userName = $("#userName");
sex = $("#sex");
birthYear = $("#birthYear");
grade = $("#grade");
collegeID = $("#collegeID");
submitBtn = $("#submit");
resetBtn = $("#reset");
/*
* 验证
* 失焦\获焦
* jquery的方法传递
*/
userID.bind("blur",function(){
$.ajax({
type:"GET",//请求类型
url:"/a",//请求的url
data:{userID:userID.val()},//请求参数
success:function(data){//data:返回数据(json对象)
if(data.toString()=== "exist" || data.toString()=== "wrong"){//账号已存在,错误提示
$("#userID").parent().removeClass("has-success");
$("#userID").parent().addClass("has-error");
$("#info-panel").removeClass("glyphicon glyphicon-ok form-control-feedback");
$("#info-panel").addClass("glyphicon glyphicon-remove form-control-feedback");
$("#userInfo").css("color","red");
$("#userInfo").html("用户为空或已存在");
}else {//账号可用,正确提示
$("#userID").parent().removeClass("has-error");
$("#userID").parent().addClass("has-success");
$("#info-panel").removeClass("glyphicon glyphicon-remove form-control-feedback");
$("#info-panel").addClass("glyphicon glyphicon-ok form-control-feedback");
$("#userInfo").css("color","green");
$("#userInfo").html("");
}
}
});
});
userName.bind("blur",function(){
if( userName.val() === "" || userName.val() == null){//用户名为空
$("#userName").parent().removeClass("has-success");
$("#userName").parent().addClass("has-error");
$("#info-panel1").removeClass("glyphicon glyphicon-ok form-control-feedback");
$("#info-panel1").addClass("glyphicon glyphicon-remove form-control-feedback");
$("#nameInfo").css("color","red");
$("#nameInfo").html("用户名为空")
}else {
$("#userName").parent().removeClass("has-error");
$("#userName").parent().addClass("has-success");
$("#info-panel1").removeClass("glyphicon glyphicon-remove form-control-feedback");
$("#info-panel1").addClass("glyphicon glyphicon-ok form-control-feedback");
$("#nameInfo").css("color","green");
$("#nameInfo").html("")
}
});
submitBtn.bind("click",function(){
if(userID.attr("validateStatus") != "true"){
userID.blur();
}else if(userName.attr("validateStatus") != "true"){
userName.blur();
}else{
if(confirm("是否确认提交数据")){
$("#editfrom").submit();
}
}
});
resetBtn.on("click",function(){
if(referer != undefined
&& null != referer
&& "" != referer
&& "null" != referer
&& referer.length > 4){
window.location.href = referer;
}else{
history.back(-1);
}
});
});
- 加入Shiro获取登录权限
对于不同的用户需要显示不一样的界面,用户角色的获取用的就是shiro
先编写基本的配置类,继承自AuthorizingRealm类:
package com.trista.realm;
import com.trista.pojo.Role;
import com.trista.pojo.UserLogin;
import com.trista.service.RoleServiceImpl;
import com.trista.service.UserLoginServiceImpl;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.Set;
@Component
public class LoginRealm extends AuthorizingRealm {
@Autowired
private UserLoginServiceImpl userLoginService;
@Autowired
private RoleServiceImpl roleService;
/**
* 获取身份信息,我们可以在这个方法中,从数据库获取该用户的权限和角色信息
* 当调用权限验证时,就会调用此方法
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String username = (String) getAvailablePrincipal(principalCollection);
Role role = null;
try {
UserLogin userLogin = userLoginService.queryUserLoginByName(username);
role = roleService.queryRoleById(userLogin.getRole());
} catch (Exception e) {
e.printStackTrace();
}
//通过用户名从数据库获取权限/角色信息
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set<String> set = new HashSet<String>();
if (role != null){
set.add(role.getRoleName());
info.setRoles(set);
}
return info;
}
/**
* 在这个方法中,进行身份验证
* login时调用
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//用户名
String username = (String) authenticationToken.getPrincipal();
//密码
String password = new String((char[]) authenticationToken.getCredentials());
UserLogin userLogin = null;
try {
userLogin = userLoginService.queryUserLoginByName(username);
} catch (Exception e) {
e.printStackTrace();
}
if (userLogin == null){
//没有该用户名
throw new UnknownAccountException();
}else if (!password.equals(userLogin.getPassword())){
throw new IncorrectCredentialsException();
}
//身份验证通过,返回一个身份信息
AuthenticationInfo aInfo = new SimpleAuthenticationInfo(username, password, getName());
return aInfo;
}
}
然后通过SecurityUtils.getSubject()获取用户信息,从而判断属于哪一类用户:
@Controller
public class LoginController {
//登录跳转
@RequestMapping(value = "/login", method = {RequestMethod.GET})
public String loginUI() throws Exception {
return "../../login";
}
@RequestMapping(value = "/login",method = {RequestMethod.POST})
public String login(UserLogin userLogin){
//shiro实现用户登录
UsernamePasswordToken token = new UsernamePasswordToken(userLogin.getUserName(), userLogin.getPassword());
Subject subject = SecurityUtils.getSubject();
//如果获取不到用户名就是登录失败,但登录失败的话,会直接抛出异常
subject.login(token);
if (subject.hasRole("admin")){
return "redirect:/admin/showStudent";
}else if (subject.hasRole("teacher")){
return "redirect:/teacher/showCourse";
}else if (subject.hasRole("student")){
return "redirect:/student/showCourse";
}
return "../../login";
}
}
至此所有功能就开发完毕!