这篇文章讲的内容是在之前spring + mybatis + spring-mvc + freemarker框架整合的代码的基础上。有需要的可以看看我博客的前两篇文章。

另外,本文章所讲相关所有代码都已上传至github上:https://github.com/SonnAdolf/sonne_game


shiro是一个很有名的安全框架,功能也很多:登录的身份认证、权限管理、session会话管理、加密、缓存等等……

至于我目前开发的网站,需要用到的功能就三点:登录、权限、session。接下来我也只围绕这三点讲。

先谈我的需求,我的网站会有三种类型的人在使用,一是游客(未登录),二是普通用户、三是管理员。

我的设定是,网站主页任何用户都可以访问,个人空间只能登录用户访问,管理员页面只能管理员用户访问。

在user类里有这样一个字段:private boolean is_admin;用于区别于普通用户和管理员。


1,加入jar包、shiro-core-1.2.3.jar、shiro-spring-1.2.3.jar、shiro-web-1.2.3.jar

jackson-annotations-2.1.4.jar、jackson-core-2.1.4.jar、jackson-databind-2.1.4.jar(json相关,由于加入登录功能会遇到前端ajax请求后端返回json的情况)

log4j-1.2.16.jar

还有些jar包是我前两期博文写过的,这次就不提了。

2,web.xml里设置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>*.form</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>*.ftl</url-pattern>
</filter-mapping>

有了这个拦截器以后,前端后端请求都可以被shiro获取。

3,新建文件,spring-shiro.xml。

这是spring类型的配置文件,在web.xml里,下面这句配置

<context-param>
                 <param-name>contextConfigLocation</param-name>
                 <param-value>classpath:/spring-*.xml</param-value>
          </context-param>

把所有spring-*.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-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"
default-lazy-init="true"> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="" />
<property name="successUrl" value="" />
<property name="unauthorizedUrl" value="" />
<property name="filterChainDefinitions">
<value>
/admin/** = roles[admin]
/space/** = roles[user]
</value>
</property>
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="authenticationRealm" />
</bean> <bean id="authenticationRealm" class="sonn.web.my_shiro.MyRealm">
</bean>
</beans>

shiroFilter里面设置的loginUrl一类的我暂时都没填。filterChainDefinitions在我看来是重点,可以实现配置的指定url的权限管理。/admin/**=roles[admin]是指/admin/**路径需要admin权限。

securityManager是shiro核心概念之一。安全管理器。支配着所有subject(用户)的和安全相关的操作。可以视作为service层。subject是entity层。

authenticationRealm是另一个核心概念,realm。类似于dao层。这里的realm是自定义的,路径为sonn.web.my_shiro.MyRealm

4,自定义Realm

package sonn.web.my_shiro;

import javax.annotation.Resource;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject; import sonn.web.entity.User;
import sonn.web.mapper.UserMapper;
import sonn.web.utils.Principal; /**
* @ClassName: AuthenticationRealm
* @Description: Shiro's realm
* @author sonne
* @date 2017-1-15 13:08:59
* @version 1.0
*/
public class MyRealm extends AuthorizingRealm { @Resource(name = "userMapper")
private UserMapper userMapper; @Override
public String getName() {
return "AuthenticationRealm";
} @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
SimpleAuthorizationInfo simpleAuthorInfo = new SimpleAuthorizationInfo();
Session session = this.getSession();
if (session == null) {
return null;
}
Principal principal = (Principal) session.getAttribute("currentUser");
String role = principal.getRole();
if (role.equals("user")) {
simpleAuthorInfo.addRole("user");
}
if (role.equals("admin")) {
simpleAuthorInfo.addRole("admin");
}
return simpleAuthorInfo;
} @Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
String password = new String((char[]) token.getCredentials());
User db_usr = userMapper.findByUsername(username);
if (!db_usr.getUsrname().equals(username)) {
throw new UnknownAccountException();
}
if (!db_usr.getPasswd().equals(password)) {
throw new IncorrectCredentialsException();
}
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
username, password, getName());
String role;
if (db_usr.isIs_admin()) {
role = "admin";
} else {
role = "user";
}
Principal principal = new Principal(db_usr.getId(), username, role);
this.setSession("currentUser", principal);
return simpleAuthenticationInfo;
} private void setSession(Object key, Object value) {
Subject currentUser = SecurityUtils.getSubject();
if (null != currentUser) {
Session session = currentUser.getSession();
System.out
.println("Session默认超时时间为[" + session.getTimeout() + "]毫秒");
if (null != session) {
session.setAttribute(key, value);
}
}
} private Session getSession() {
Subject currentUser = SecurityUtils.getSubject();
if (null != currentUser) {
Session session = currentUser.getSession();
return session;
}
return null;
}
}

这里面setSession和getSession就是shiro的session的get、set方法,不多说。

核心是AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0)方法和AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException

。也就是继承AuthorizingRealm方法必须要实现的方法

前者是每次用户访问权限限制url(见第三步filterChainDefinitions里的设置)都会访问这个方法,来检查是否具有权限。代码中通过获取session,然后检查session中设置的权限。(用户登录成功后设置session这是前提)

后者是登录过程通过用户输入的用户名和密码进行权限确认的方法。用户名和密码通过AuthenticationToken token参数获取。之后查询数据库检验用户信息,若登录成功则设置session信息。

        Principal principal = new Principal(db_usr.getId(), username, role);
this.setSession("currentUser", principal);

session中包含用户id、用户名、和角色。

至于mybatis的数据库操作,通过spring标签将mapper的bean注入进来了,之后直接使用即可:

    @Resource(name = "userMapper")
private UserMapper userMapper;

有必要注明一点:由于目前还没完全地做成一个登录功能,所以登录过程没有加密处理,也没有验证码。最主要目的是整合shiro框架。

5,登录controller

package sonn.web.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody; import sonn.web.entity.User; import com.alibaba.fastjson.JSONObject; /**
* @ClassName: LoginController
* @Description: Controller of login
* @author sonne
* @date 2017-1-15 13:07:00
* @version 1.0
*/
@Controller
@RequestMapping("/login")
public class LoginController { /*
* show the web page of login action.
*/
@RequestMapping(value = "/show", method = RequestMethod.GET)
public String submit(HttpServletRequest request, Model model)
throws Exception {
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName()
+ ":" + request.getServerPort() + path;
model.addAttribute("base", basePath);
return "login";
} /*
* login submit, check, save the session.
*/
@RequestMapping(value = "/login", method = RequestMethod.POST)
@ResponseBody
public JSONObject submit(HttpServletRequest request,
HttpServletResponse response, User usr) throws Exception {
JSONObject jo = new JSONObject();
String usrname = usr.getUsrname();
String passwd = usr.getPasswd();
UsernamePasswordToken token = new UsernamePasswordToken(usrname, passwd);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
} catch (IncorrectCredentialsException ice) {
jo.put("success", false);
jo.put("msg", "密码错误");
return jo;
} catch (UnknownAccountException uae) {
jo.put("success", false);
jo.put("msg", "未知用户名");
return jo;
} catch (ExcessiveAttemptsException eae) {
jo.put("success", false);
jo.put("msg", "登录次数过多");
return jo;
}
jo.put("success", true);
jo.put("msg", "登录成功");
return jo;
} }

这里通过设置UsernamePasswordToken这个token给Realm来进行校验:

        String usrname = usr.getUsrname();
String passwd = usr.getPasswd();
UsernamePasswordToken token = new UsernamePasswordToken(usrname, passwd);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
} catch (IncorrectCredentialsException ice) {
……
}

Subject subject = SecurityUtils.getSubject();这样的写法应该是工厂模式吧。

6,登录功能相关、新增根据用户名查询用户的dao层操作

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace = "sonn.web.mapper.UserMapper">
<select id = "findAll" resultType = "sonn.web.entity.User">
select ID,USRNAME,PASSWD,IS_ADMIN from USER
</select>
<select id = "findByUsername" parameterType="String" resultType = "sonn.web.entity.User">
select ID,USRNAME,PASSWD,IS_ADMIN from USER where USRNAME = #{usrname}
</select>
</mapper>

需要注意mybatis里select ID,USRNAME,PASSWD,IS_ADMIN from USER where USRNAME = #{usrname}这样的写法。

7,登录功能前端方面、一些与shiro不太相关的介绍:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>sonne_game</title>
</head>
<body>
<p>Here,log in</p>
<form id="loginForm" action="/Sonne_game/login/login.form" method="post">
usrname:<input name="usrname"/><br>
passwd:<input name="passwd"/><br>
<button type="submit" id="submit" style="height:20px;width:55px;">submit</button>
</form>
<script type="text/javascript" src="${base}/Jquery/jquery-1.3.1.js"></script>
<script type="text/javascript" src="${base}/Jquery/jquery.form.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#loginForm').ajaxForm({
dataType: 'json',
beforeSubmit: validate,
success: successFunc
});
});
function validate(formData, jqForm, options) {
return true;
}
function successFunc(data) {
if (data.success) {
alert("登录成功:"+" " + data.msg);
}
else {
alert("登录失败:"+" " + data.msg);
}
}
</script>
</body>
</html>

登录这类操作操作需要使用ajax操作。这方面原始的javascript是比较麻烦的。一般用jquery的组件。jquery.form.js。具体先不讲了。

本系列下一篇大概会认真搞一搞前端,做一个全力发挥自己前端水平的登录页面(虽然不是前端程序员)。

主要会研究下响应式页面和滑动式验证码~

8,目前达到的效果:

项目结构:

登录:

只有admin类型的user能访问admin管理页面,同理只有普通用户能访问个人空间页面:

spring-mvc + shiro框架整合(sonne_game网站开发04)的更多相关文章

  1. spring-mvc+freemarker整合(sonne_game网站开发03)

    今天的任务就是在spring+mybatis+springmvc的基础上,将freemarker整合进来. freemarker是什么? freemarker是一种模板引擎.它的目的是基于模板和数据, ...

  2. Spring + Spring MVC+Hibernate框架整合详细配置

    来源于:http://www.jianshu.com/p/8e2f92d0838c 具体配置参数: Spring: spring-framework-4.2.2Hibernate: hibernate ...

  3. Spring + Spring MVC + MyBatis框架整合

    ---恢复内容开始--- 一.Maven Web项目创建 如有需要,请参考:使用maven创建web项目 二.Spring + Spring MVC + MyBatis整合 1.Maven引入需要的J ...

  4. Spring+Spring MVC+Mybatis 框架整合开发(半注解半配置文件)

    项目结构: (代码里面都有注释) 一.在pom文件中依赖jar包 因为我这里分了模块,所以有父子级的共两个pom文件 父级: <?xml version="1.0" enco ...

  5. Spring MVC + jpa框架搭建,及全面分析

    一,hibernate与jpa的关系 首先明确一点jpa是什么?以前我就搞不清楚jpa和hibernate的关系. 1,JPA(Java Persistence API)是Sun官方提出的Java持久 ...

  6. Spring MVC 学习总结(十)——Spring+Spring MVC+MyBatis框架集成(IntelliJ IDEA SSM集成)

    与SSH(Struts/Spring/Hibernate/)一样,Spring+SpringMVC+MyBatis也有一个简称SSM,Spring实现业务对象管理,Spring MVC负责请求的转发和 ...

  7. [读后感]spring Mvc 教程框架实例以及系统演示下载

    [读后感]spring Mvc 教程框架实例以及系统演示下载 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致&qu ...

  8. hibernate+spring+mvc+Easyui框架模式下使用grid++report的总结

    最近刚开始接触hibernate+spring+mvc+Easyui框架,也是刚开通了博客,希望能记录一下自己实践出来的东西,让其他人少走弯路. 转让正题,以个人浅薄的认识hibernate对于开发人 ...

  9. SSH(Spring SpringMVC Hibernate)框架整合

    项目说明: 使用SSH(Spring SpringMVC Hibernate)框架整合添加部门功能 项目结构   1.导入依赖jar包 <!--单测--> <dependency&g ...

随机推荐

  1. JS复选框选中

    Web前端之复选框选中属性   熟悉web前端开发的人都知道,判断复选框是否选中是经常做的事情,判断的方法很多,但是开发过程中常常忽略了这些方法的兼容性,而是实现效果就好了.博主之前用户不少方法,经常 ...

  2. SQL SERVER2005 excel float导入

    接到mission:将一堆excel的东西导入到SQL SERVER2005 命令很easy SELECT * INTO XLImport3 FROM OPENDATASOURCE('Microsof ...

  3. 【转】android创建Popwindow弹出菜单的两种方式

    方法一的Activity package com.app.test02; import android.app.Activity; import android.os.Bundle; import a ...

  4. 用javascript实现2048的小游戏

    前段时间,看了一个视频,用javascript实现的2048小游戏,发现不难,都是一些基出的语法和简单逻辑. 整个2048游戏没有很多的数据,所有,实现起来还是很有成就感的. 先上图,简直就和原版游戏 ...

  5. 在Ubuntu上安装 nginx, MySQL, PHP (LEMP),phpmyadmin和WordPress

    0)更新 Apt-Get 终端命令:sudo apt-get update 1) 安装php sudo apt-get install php5 2)安装MySql 终端命令: sudo apt-ge ...

  6. c# in deep 之Lambda表达式于LINQ表达式结合后令人惊叹的简洁(2)

    当Lambda表达式和LINQ一起使用时,我们会发现原本冗长的代码会变得如此简单.比如我们要打印0-10之间的奇数,让其从高到低排列并求其平方根,现在只用一行代码即可完成其集合的生成,直接上代码: v ...

  7. 历年noip复赛试题整合

    早晨打算把历年的试题都过一遍,整理一下大概会往哪个方向考,考什么,不说太多,开始吧 2013: Day1: T1 转圈游戏 : 快速幂(关键在于要会打 快速幂) 思路:因为每次都进m位,相当于每次x加 ...

  8. OA小助手

    基于 WPF + Modern UI 的 公司OA小助手 开发总结 前言: 距离上一篇博客,整整一个月的时间了.人不能懒下来,必须有个阶段性的总结,算是对我这个阶段的一个反思.人只有在总结的过程中才会 ...

  9. 关于UITextfield弹出键盘解决方案

    解决的问题:当你点击一个UITextfield时,不想让其弹出键盘,如果你觉得不就是取消其第一响应者嘛,resignRespond一下不就行了嘛,确实,如果你只是在其编辑完成后让其键盘消失,那这个就够 ...

  10. URL 调度器(URL dispatcher)

    URL 调度器(URL dispatcher) 在刚开始接触 django 的时候, 我们尝试着从各种入门文档中创建一个自己的 django 项目, 需要在 mysite.urls.py 中配置 UR ...