在线考试系统SSM+shiro

本文介绍了一个基于SSM框架和Shiro安全框架的在线考试系统,涵盖登录注册、试题管理、随机生成试卷及学生答题等功能。详细探讨了系统结构、Shiro权限管理配置、试题添加与试卷生成的具体实现。

在线考试系统

本系统实现了对试题的怎删改查,以及随机生成试卷,考生答题等功能
用时一周,只实现了基本功能,后期还将继续完善

项目结构

在这里插入图片描述

登录注册

使用了shiro安全框架,对本系统进行权限管理下面来看一看具体的代码

UserRealm

public class UserRealm extends AuthorizingRealm {

    private UserService userService;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String)principals.getPrimaryPrincipal();

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(userService.findRoles(username));
        authorizationInfo.setStringPermissions(userService.findPermissions(username));
        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        String username = (String)token.getPrincipal();

        User user = userService.findByUsername(username);

        if(user == null) {
            throw new UnknownAccountException();//没找到帐号
        }

        if(Boolean.TRUE.equals(user.getLocked())) {
            throw new LockedAccountException(); //帐号锁定
        }

        //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以自定义实现
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user.getUsername(), //用户名
                user.getPassword(), //密码
                ByteSource.Util.bytes(user.getCredentialsSalt()),//salt=username+salt
                getName()  //realm name
        );
        return authenticationInfo;
    }

    @Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }

    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }

    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }

    public void clearAllCachedAuthorizationInfo() {
        getAuthorizationCache().clear();
    }

    public void clearAllCachedAuthenticationInfo() {
        getAuthenticationCache().clear();
    }

    public void clearAllCache() {
        clearAllCachedAuthenticationInfo();
        clearAllCachedAuthorizationInfo();
    }

}

在shiro.xml中配置权限
在这里插入图片描述
这个框架还是蛮好用的,它会自动记录用户登录信息,以及判断用户登录状态,方便了我们对系统的权限控制。
同时前端也可以加入shiro标签,用户处于登录或非登录状态显示内容有所不同
如下

 <shiro:guest>
                <h3>欢迎游客访问,<a href="${pageContext.request.contextPath}/login.jsp"><button class="btn btn-primary">点击登录</button> </a></h3><br/>
            </shiro:guest>
            <shiro:user>
              <h3>欢迎[<shiro:principal/>]登录,<a href="${pageContext.request.contextPath}/logout"><button class="btn btn-danger">点击退出</button> </a></h3>  <br/>
             <h3><a href="/toMyPage"><button class="btn btn-primary">点击前往</button></a></h3>
            </shiro:user>

其中遇到了一个问题,在使用shiro框架的缓存功能时,会莫名其妙的生成多个缓存文件,报错只告诉我配置错误。

解决方法如下

建议大家使用2.4.8版本

<dependency>
      <groupId>net.sf.ehcache</groupId>
      <artifactId>ehcache-core</artifactId>
      <version>2.4.8</version>
    </dependency>

添加试题功能

效果图
在这里插入图片描述
我在这个最初的功能上卡了一段时间,因为我不仅要将试题放到数据库中,还需要将它出成试卷,不能一股脑的将试题内容放进去。
其他人的项目中有一种方法,将试题转换成xml语言放入数据库,调用时,再转化成html语言,对我来说难度太高。于是我就想将整道试题的html页面的html代码存入数据库,但是考虑到出题页面和做题页面形式不同会比较麻烦,所以就取了个比较low的方法。
将试题题目,选项,答案分别插入数据库,如果是简答题选项就为空,出试卷时就分别遍历一遍。下面是controller中具体实现的代码

public String addQuestion(TQuestion question, HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
        response.setContentType("text/html;charset=utf-8");
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        String loginName = (String) SecurityUtils.getSubject().getPrincipal();
        Message message=new Message();
        question.setCreatTime(new Date());
        question.setCreator(loginName);
        try{
            questionService.addQuestion(question);
            request.setAttribute("message","添加成功");
        }catch (Exception e){
            request.setAttribute("message",e.getClass().getName());
//            message.setResult("error");
//            message.setMessageInfo(e.getClass().getName());
            e.printStackTrace();
        }
        return "admin/questionAdd";
    }
    @RequestMapping(value = "/admin/questionList", method = RequestMethod.GET)
    public String questionListPage(@RequestParam(defaultValue = "1",required = true,value = "pageNo")Integer pageNo, Model model){
        Integer pageSize=10;
        PageHelper.startPage(pageNo,pageSize);
        List<TQuestion> questionList=questionService.getQuestionList();
        PageInfo<TQuestion> pageInfo=new PageInfo<>(questionList);

        model.addAttribute("questionList",questionList);
        return "admin/questionList";

    }

随机生成试卷

输入每个题型的试题数,从数据库中查询相应数量的试题生成试卷
以下是controller层的代码

public String addPaper(@RequestParam int selectNum, @RequestParam int multipleNum,
                           @RequestParam int descNum, TPaper paper, Model model, HttpSession session){
        Map map=new HashMap();
        List<TQuestion> selectList = null;
        List<TQuestion> multipleList = null;
        List<TQuestion> descList = null;
        List<TQuestion> paperList = new ArrayList<TQuestion>();
        if(selectNum>0){//选择题
            map.put("num", selectNum);
            map.put("typeId", 1);
            selectList = questionService.creatPaper(map);
            paperList.addAll(selectList);
        }
        if(multipleNum>0){//判断题
            map.put("num", multipleNum);
            map.put("typeId", 4);
            multipleList = questionService.creatPaper(map);
            paperList.addAll(multipleList);
        }
        if(descNum > 0 ){//描述题
            map.put("num", descNum);
            map.put("typeId", 5);
            descList = questionService.creatPaper(map);
            paperList.addAll(descList);
        }
        String quesId = "";
        for(TQuestion ques : paperList){
            quesId+=ques.getId()+",";
        }
        if(!quesId.isEmpty()){
            quesId = removeLast(quesId);
        }
        paper.setQuestionId(quesId);
        paper.setPaperstate("0");
        paperService.insert(paper);
        return "redirect:/teacher/toPaperList";
    }

学生答卷

通过之前插入的试题id依次遍历,查询试题表中的内容,根据其实体类型分别传向前端
以下

public String qryPaperDetail(String id, Model model, HttpSession session, HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
        response.setContentType("text/html;charset=utf-8");
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
        Map map = new HashMap();
        map.put("id", id);
        TPaper paper = paperService.getPaperDetail(map);
        String []ids = paper.getQuestionId().split(",");
        List<TQuestion> selList = new ArrayList<TQuestion>();
        List<TQuestion> inpList = new ArrayList<TQuestion>();
        List<TQuestion> desList = new ArrayList<TQuestion>();
        for(int i = 0;i<ids.length;i++){
            int li=Integer.parseInt(ids[i]);
            TQuestion question = questionService.findQuestionById(li);
            if(question.getTypeId()==1){//单选
                selList.add(question);
            }
            if(question.getTypeId()==2){//填空
                inpList.add(question);
            }
            if(question.getTypeId()==4){//简答题
                desList.add(question);
            }
        }

        if(selList.size()>0){
            model.addAttribute("selectQ", "单项选择题(每题5分)");
            model.addAttribute("selList", selList);
        }

        if(inpList.size()>0){
            model.addAttribute("inpQ", "多选题(每题5分)");
            model.addAttribute("inpList", inpList);
        }

        if(desList.size()>0){
            model.addAttribute("desQ", "简答题(每题5分)");
            model.addAttribute("desList", desList);
        }

        model.addAttribute("paper", paper);
        return "/student/paper-detail";
    }

jsp页面将不同页面遍历出来

<div class="container">
                                <!-- 试卷名称 -->
                                <h2 class="bars" align="center"><font color="blue">${paper.papername }</font></h2>
                                <div class="input-group">
                                    <h4 class="bars" align="left"><font color="blue">${selectQ}</font></h4>
                                </div>

                                <c:forEach items="${selList}" var="selType">
                                    <p><h4 class="bars" align="left">${selType.name }</h4></p>
                                    <div class="input-group">
                                        <input name="${selType.id }" type="radio" value="A" checked="checked"/><font size="4">${selType.optiona }</font></br>
                                        <input name="${selType.id }" type="radio" value="B"/><font size="4">${selType.optionb }</font></br>
                                        <input name="${selType.id}" type="radio" value="C"/><font size="4">${selType.optionc}</font></br>
                                        <input name="${selType.id }" type="radio" value="D"/><font size="4">${selType.optiond }</font></br>
                                            <%--  					<p><h4 class="bars"><font color="blue">我的答案:${errorBook.userAnswer } </font></h4></p>
                                                                <p><h4 class="bars">标准答案:${errorBook.question.answer }( ${errorBook.question.answerDetail })</h4></p>
                                                                <p><h4 class="bars"><font color="red">解析:${errorBook.question.remark }</font></h4></p> --%>
                                    </div>

总结

其实和其他人的项目大同小异,而且因为是紧迫只完成了基本功能。
还是要注意Mybatis映射,我会经常在这上面出错。
感觉自己在前期设计项目时做的还算不错,在开始编写前就把项目的每个功能模块化。
但是我有些急功近利,刚开始一股脑的把后台接口都写完,然后运行后就会发现一大堆的报错,改错的时间其实比编码时间要长得的多。做什么事还是不能太急于求成,一步一步脚踏实地得来。
就这样吧~

使用到的技术:redis、amcharts、maven、html5、ajax、js、jquery以及css,关系型数据库采用的是mysql。 文件夹中有可以直接导入使用的数据库,以及可以导入试卷的excel表格格式. 该项目分为学生模块,和教师模块。 教师模块:教师可以通过导入Excel表格的方式进行添加试卷,如果Excel表中有不合法的数据,会在前台提醒哪一行哪一列出了什么问题,添加试卷后,教师可以发布试卷,试卷发布后,学生就可以答题,每张试卷都有作答时长,作答时间结束,将会自动提交试卷。考试结束后,教师可以发布答案。对于修改试卷,教师可以先选择所要修改的试卷,对于试卷可以修改试卷的名称以及考试时长,要想修改试题可以点击编辑试题,进行批量修改。 学生模块:注册登录进入学生考试平台,选择考卷,进行作答,试卷分为单选题、多选题以及判断题,分值各不相同,对于多选题错答不得分,漏答得一半的分。在作答期间,学生可以先保存,保存的内容存储在Redis中。若点击提交,提交后直接显示成绩。提交后就不能再进入考试。要想看正确答案,得等到考试结束,教师发布成绩后,才可以看到。 学生可以看到自己的作答历史,每道题之前学生的答案以及该题正确的答案都很清晰的标注出来。为了方便学生统计自己的成绩,本系统采用了amcharts技术根据学生的历次成绩制作了柱状图和折线图结合的图表。学生可以很直观地看到自己成绩的波动。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值