一.mavne的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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.tiglle</groupId>
<artifactId>springmvc-shiro</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>springmvc-shiro Maven Webapp</name>
<url>http://maven.apache.org</url>
<!-- 全局属性 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>3.1.2.RELEASE</spring.version>
</properties>
<dependencies>
<!-- springmvc的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- shiro-spring整合 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/jstl/jstl -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- spring开启自动代理需要的包 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.5</version>
</dependency>
<!-- shiro缓存:结合ehcache -->
<!-- https://mvnrepository.com/artifact/net.sf.ehcache/ehcache-core -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.11</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.0</version>
</dependency>
<!-- scope=provicer:在编译和测试的过程有效,最后生成war包时不会加入,诸如:servlet-api,因为servlet-api,tomcat等web服务器已经存在了,如果再打包会冲突 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>springmvc-shiro</finalName>
</build>
</project>
二.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_3_0.xsd" version="3.0">
<display-name>Archetype Created Web Application</display-name>
<!-- Spring MVC 核心 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--以下init-param是自定义SpringMVC的配置文件的位置 -->
<!--
1.可以通过contextConfigLocation来自定义SpringMVC配置文件的位置,如不指定,则默认在WEB-INF目录下,名称为[<servlet-name>]-servlet.xml,此时文件名必须为[<servlet-name>]-servlet.xml,否则会出错
2.如果使用了contextConfigLocation自定义SpringMVC配置文件的位置,spring便不会加载默认/WEB-INF/[<servlet-name>]-servlet.xml文件,需注意
-->
<!-- <init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param> -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 拦截设置 -->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- 此处可以可以配置成*.do,对应struts的后缀习惯 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- Spring MVC 核心配置结束 -->
<!--配置监听器-->
<!-- 读取Spring配置文件;applicationContext-shiro.xml -->
<context-param>
<param-name>contextConfigLocation</param-name>
<!-- 如果是监听多个文件,可用‘,’隔开-->
<param-value>classpath:applicationContext-shiro.xml</param-value>
</context-param>
<!--Spring监听器 ApplicationContext 载入 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Spring字符集过滤器 -->
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- shiro配置 -->
<!-- shiro的filter -->
<filter>
<!-- 任意,但注入spring的时候ref名字要和这个相同,或者通过targetBeanName修改 -->
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<!-- 是否由servlet容器控制filter的生命周期true:是 -->
<init-param>
<!-- 覆盖DelegatingFilterProxy的属性默认配置或赋值 -->
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
<!-- 修改spring容器注入filter时的id名字,如果不设置,则用filter-name标签的值 -->
<init-param>
<param-name>targetBeanName</param-name>
<param-value>shiroFilter</param-value>
</init-param>
</filter>
<!-- 过滤设置 -->
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
三.springmvc的配置文件springmvc-servlet.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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.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"
default-lazy-init="true">
<!-- 开启注解模式驱动 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 扫包 -->
<context:component-scan base-package="com.tiglle.*"></context:component-scan>
<!-- 开启shiro注解的配置 -->
<!-- spring方面:开启aop代理 -->
<aop:config proxy-target-class="true"></aop:config>
<!-- shiro方面:开启shiro注解支持 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<!-- 视图渲染 jsp/freemaker/velocity-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 制定页面存放的路径 -->
<property name="prefix" value="/"></property>
<!-- 文件的后缀 -->
<property name="suffix" value=".jsp"></property>
</bean>
<!-- permission注解不生效 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>
<!-- permission注解不生效 -->
<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>
四.springmvc和shiro的整合配置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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
default-lazy-init="true">
<!-- 将web.xml配置的shiroFilter注入到spring容器
id和web.xml的filter-name的值相同,或者和targetBeanName值相同
-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 覆盖或赋值ShiroFilterFactoryBean的属性 -->
<!-- 指定securityManager -->
<property name="securityManager" ref="securityManager"/>
<!-- 认证提交地址,如果用户没有认证或认证失效,需要进行表单提交请求此地址进行身份认证(由formAuthentication进行表单认证)默认会自动寻找Web工程根目录下的"/login.jsp"页面 -->
<property name="loginUrl" value="/login"/>
<!-- 指定没有权限时跳转的页面(或者一个控制器的连接) -->
<property name="unauthorizedUrl" value="/refuse.jsp"/>
<!-- 指定认证成功的跳转页面,如果不设置,默认跳回上一个url-->
<property name="successUrl" value="/WEB-INF/page/main.jsp"/>
<!-- shiro的自定义filter配置,会从上往下一个个过滤 -->
<property name="filters">
<map>
<!-- 所有自定义filter都在这配置,将自定义的FormAuthenticationFilter注入shiro的filter中 -->
<entry key="authc" value-ref="formAuthenticationFilter" />
</map>
</property>
<!-- 过滤规则,从上到下顺序执行,一般将/**放在最下面 -->
<property name="filterChainDefinitions">
<value>
/=anon//web.xml配置了welcome-file-list的话,加上这个才能访问
<!-- /**:所有的地址 anon:匿名访问 所有地址都可以匿名访问-->
<!-- /login=authc -->
<!-- 指定访问shiro自带的退出登录的连接,shiro会清除session /logout:url logout:shiro自带的退出登陆的方法-->
/logout=logout
<!-- 首页可以匿名访问 -->
/codeImage.jsp=anon
<!-- 配置记住我或有认证信息可以访问的连接,user权限就是 -->
/main = user <!-- 只有main能在记住我状态和用户信息不为空的情况下能访问,其他连接会往下走authc认证 -->
<!--
需要授权的连接 格式:连接 = perms[权限字符串标示] ,认证失败,会跳转unauthorizedUrl的地址,只有这里配置的连接才会去realm检测权限
缺点:配置太多
解决方法:注解和jsp标签方式配置,注解需要在spring配置文件开启,然后在controller上添加注解
-->
<!-- /items/query = perms[item:query] --><!-- 换成使用注解方式配置 -->
<!-- /**:所有的地址 authc:需要认证 -->
/**=authc
</value>
</property>
</bean>
<!-- securityManager安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- 使用自定义realm认证 -->
<property name="realm" ref="customRealm"/>
<!-- 注入缓存管理器,第一次授权会请求realm,后面的授权都不会请求realm -->
<property name="cacheManager" ref="cacheManager" />
<!-- 注入session管理器 -->
<property name="sessionManager" ref="sessionManager"/>
<!-- 注入记住我remeberMeManager管理器 -->
<property name="rememberMeManager" ref="rememberMeManager"/>
</bean>
<!-- 自定义realm -->
<bean id="customRealm" class="com.tiglle.shiro.CustomRealm">
<!-- 将凭证匹配器注入到realm-->
<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
<!-- 配置凭证匹配器:指定散列算法,散列次数 -->
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- 散列算法 -->
<property name="hashAlgorithmName" value="md5"/>
<!-- 散列次数 -->
<property name="hashIterations" value="1"/>
</bean>
<!-- ehcache缓存管理器 value为ehcache配置文件位置-->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml" />
</bean>
<!-- 会话管理,session交给shiro管理 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!-- session的超时时间 毫秒-->
<property name="globalSessionTimeout" value="500000"/>
<!-- 是否删除已经失效的session数据 -->
<property name="deleteInvalidSessions" value="true"/>
</bean>
<!-- 配置form认证过滤器为自定义的,不配置会使用系统默认的,也可以直接配置系统默认的,并修改账号密码的字段名 -->
<bean id="formAuthenticationFilter" class="com.tiglle.shiro.CustomFormAuthenticationFilter">
<!-- 设置表单中账号的input的name值 -->
<property name="usernameParam" value="username"/>
<!-- 设置表单中密码的input的name值 -->
<property name="passwordParam" value="password"/>
<!-- 设置记住我的input的name值 -->
<property name="rememberMeParam" value="rememberMe"/>
</bean>
<!-- remenerMeManager管理器,写cookie,取cookie,生成用户信息 -->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<property name="cookie" ref="rememberMeCookie" />
</bean>
<!-- 记住我cookie信息 -->
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<!-- 生成cookie的名字 -->
<constructor-arg value="remeberMe" />
<!-- cookie的存在时间(30天) 秒-->
<property name="maxAge" value="2592000"/>
</bean>
</beans>
五.ehcache的缓存配置shiro-ehcache.xml:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<!-- diskStore:缓存数据持久化的目录地址 -->
<diskStore path="F:\ehcache\store" />
<defaultCache
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
diskSpoolBufferSizeMB="30"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
六.自定义reaml的java类和自定义Form认证过滤器:
1.自定义reaml的java类CustomRealm.java:
package com.tiglle.shiro;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.SimpleAuthenticationInfo;
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.util.ByteSource;
import com.tiglle.bean.User;
/**
* 自定义realm,覆盖默认realm(需要配置),让授权数据从数据库或其他地方获取
* @author Administrator
*
*/
public class CustomRealm extends AuthorizingRealm{
/**
* 设置realm的名称,模拟用
*/
@Override
public void setName(String name) {
super.setName("customRealm");
}
/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//token是用户输入的信息
//1.从token中取出用户身份信息,也就是登陆页面的username
String username = (String) token.getPrincipal();
System.out.println("---username:"+username);
//2.根据用户身份信息从数据库查询用户信息
//.......根据userCode查询数据库
//模拟查了数据库,获得了User对象
User user = new User();
//User user = userService.getUserByUsername(username);
//模拟从数据库查询了用户信息,设置用户信息
user.setNickname("小明");
user.setUname(username);
user.setUpass("bacbc96d57da0555dc8b58beadbc2d93");//123456+qwert
user.setSalt("qwert");
//3.查询不到,请返回null
if(user==null){
return null;
}
//模拟从数据库查询了用户菜单信息
Map<String,String> menus = new HashMap<String, String>();
//Map<String,String> menus = userService.getUserMenusById(user.getId());
menus.put("商品列表","items/query");
menus.put("增加商品","items/toAdd");
menus.put("编辑商品","items/toUpdate");
user.setMenus(menus);
//密码
String password = user.getUpass();
//盐(盐在密码前面。。。。。。)
String salt = user.getSalt();
//4.查询到,返回AuthenticationInfo,将user设置进去,盐设置j进去,别忘了在spring配置文件配置凭证匹配器,可以在控制器通过Subject获取
SimpleAuthenticationInfo simpleAuthenticationInfo =
new SimpleAuthenticationInfo(user,password,ByteSource.Util.bytes(salt),this.getName());
return simpleAuthenticationInfo;
}
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//认证成功后授权
//从principals中获取主身份信息,上面认证成功后存入的省份信息
@SuppressWarnings("unused")
User user = (User) principals.getPrimaryPrincipal();
//根据身份信息从数据库查询权限信息
//模拟查询了数据库...
//根据身份获得了用户所有的权限信息(List为获取的权限集合)
List<String> permission = new ArrayList<String>();
//List<String> permission = userService.findPermissionsByUserId(user.getId());
permission.add("user:add");//=user:create:*
permission.add("user:create:1");
permission.add("items:query");
permission.add("items:add");
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//返回用户所有的权限
simpleAuthorizationInfo.addStringPermissions(permission);
return simpleAuthorizationInfo;
}
/**
* 手动清空shiro授权缓存的方法,供service调用
* 清除后,授权会再次请求realm
*/
public void clearCached(){
PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
super.clearCache(principals);
}
}
2.自定义form认证过滤器的javaCustomFormAuthenticationFilter.java:
package com.tiglle.shiro;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
// 在这里进行验证码的校检,或者session中数据的校检
//将ServletRequest转为HttpServletRequest
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
//获取session
HttpSession session = httpServletRequest.getSession();
//取出session中的验证码
String code = (String) session.getAttribute("code");
//取出页面的验证码(从ServletRequest获取)
String pageCode = httpServletRequest.getParameter("code");
if(pageCode!=null&&!"".equals(pageCode)&&!pageCode.equals(code)){
//如果验证码为空或者验证码与session中的不相等,手动设置异常
httpServletRequest.setAttribute("shiroLoginFailure", "CodeErrorException");
//拒绝访问,不在继续验证账号密码
return true;
}
//否则,继续验证账号密码(父类方法)
return super.onAccessDenied(request, response);
}
}
七.存放用户信息的实体类User.java:
package com.tiglle.bean;
import java.io.Serializable;
import java.util.Map;
//shiro的remeberMe需要用户身份实体类实现Serializable以便序列化和反序列化
public class User implements Serializable{
private String id;
private String uname;
private String upass;
private String salt;
private String nickname;
private Map<String,String> menus;
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getUpass() {
return upass;
}
public void setUpass(String upass) {
this.upass = upass;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Map<String, String> getMenus() {
return menus;
}
public void setMenus(Map<String, String> menus) {
this.menus = menus;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
}
八.springmvc的控制器controller:
1.LoginController.java:
package com.tiglle.controller;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginController {
/**
* 登陆原理:
* 使用FormAuthenticationFilter过虑器实现 ,原理如下:
* 将用户没有认证时,请求loginurl进行认证,用户身份和用户密码提交数据到loginurl
* FormAuthenticationFilter拦截住取出request中的username和password(两个参数名称是可以配置的)
* FormAuthenticationFilter调用realm传入一个token(username和password)
* realm认证时根据username查询用户信息(在Activeuser中存储,包括 userid、usercode、username、menus)。
* 如果查询不到,realm返回null,FormAuthenticationFilter向request域中填充一个参数(记录了异常信息)
* @throws Exception
*/
//此控制器只有自定义realm认证失败才会进入,认证成功自动跳转到上一次访问的连接
@RequestMapping("login")
public String login(HttpServletRequest request) throws Exception{
//如果登陆失败,shiro把异常放入request中,key为shiroLoginFailure,value为异常的全限定名(包名.类名)
String exceptionName = (String) request.getAttribute("shiroLoginFailure");
//根据shiro返回的的异常类名判断异常信息
if(exceptionName!=null){
if(UnknownAccountException.class.getName().equals(exceptionName)){
//账号不存在异常
throw new Exception("账号不存在");
}else if(IncorrectCredentialsException.class.getName().equals(exceptionName)){
//用户名/密码错误
throw new Exception("用户名/密码错误");
}else if("CodeErrorException".equals(exceptionName)){
throw new Exception("验证码错误");
}else{
throw new Exception("未知异常");
}
}
return "WEB-INF/page/login";
}
}
2.MainController.java:
package com.tiglle.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.tiglle.bean.User;
@Controller
public class MainController {
@RequestMapping("main")
public String main(Model model){
//从shiro中获取放入的user信息
Subject subject = SecurityUtils.getSubject();
User user = (User) subject.getPrincipal();
model.addAttribute("user", user);
return "/WEB-INF/page/main";
}
}
3.ItemsController.java:
package com.tiglle.controller;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/items")
public class ItemsController {
//shiro权限注解的使用,只有拥有对应的权限才能执行控制器,否则到配置文件中配置的授权失败的连接(或者报没有权限的异常...)
//执行到注解时,会到自定义realm中授权
/**
* 到商品列表
* @return
*/
@RequestMapping("/query")
@RequiresPermissions("items:query")//shiro权限注解,表示只有items:query权限才能进此控制器
public String query(){
return "/WEB-INF/page/itemsList";
}
/**
* 到编辑商品页
* @return
*/
@RequiresPermissions("items:add")//shiro权限注解,表示只有items:add权限才能进此控制器
@RequestMapping("/toAdd")
public String toAdd(){
return "/WEB-INF/page/addItems";
}
/**
* 确定编辑商品
* @return
*/
@RequestMapping("/add")
@RequiresPermissions("items:add")//shiro权限注解,表示只有items:add权限才能进此控制器
public String add(){
return "/WEB-INF/page/itemsList";
}
/**
* 到编辑商品页
* @return
*/
@RequestMapping("/toUpdate")
@RequiresPermissions("items:update")//shiro权限注解,表示只有items:update权限才能进此控制器
public String toUpdate(){
return "/WEB-INF/page/updateItems";
}
/**
* 确定编辑商品
* @return
*/
@RequestMapping("/update")
@RequiresPermissions("items:update")//shiro权限注解,表示只有items:update权限才能进此控制器
public String update(){
return "/WEB-INF/page/itemsList";
}
}
4.TestRemoveShiroCache.java:
package com.tiglle.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.tiglle.shiro.CustomRealm;
@Controller
public class TestRemoveShiroCache {
/**
* 手动情况shiro授权缓存(shiro默认关闭认证缓存,开启授权缓存,授权缓存需配置)
* 正常手动清空授权缓存应该在service层,当用户权限改变时调用
*/
//注入自定义realm
@Autowired
private CustomRealm realm;
@RequestMapping("clearShiroCache")
public String clearShiroCache(){
realm.clearCached();
return "success";
}
}
九.webapps下的页面:
1.首页index.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<html>
<body>
<centeer>
<h1>首页面</h1>
</centeer>
<a href="main">系统管理</a>
</body>
</html>
2.没有权限配置的无权页面refuse.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!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=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
无权访问
</body>
</html>
3.生成验证码的codeImage.jsp:
<%@ page contentType="image/jpeg" import="java.awt.*,java.awt.image.*,java.util.*,javax.imageio.*" pageEncoding="utf-8"%>
<%!
Color getRandColor(int fc,int bc)
{
Random random = new Random();
if(fc>255) fc=255;
if(bc>255) bc=255;
int r=fc+random.nextInt(bc-fc);
int g=fc+random.nextInt(bc-fc);
int b=fc+random.nextInt(bc-fc);
return new Color(r,g,b);
}
%>
<%
out.clear();//这句针对resin服务器,如果是tomacat可以不要这句
response.setHeader("Pragma","No-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);
int width=60, height=20;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
Random random = new Random();
g.setColor(getRandColor(200,250));
g.fillRect(0, 0, width, height);
g.setFont(new Font("Times New Roman",Font.PLAIN,18));
g.setColor(getRandColor(160,200));
for (int i=0;i<155;i++)
{
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x,y,x+xl,y+yl);
}
String sRand="";
for (int i=0;i<4;i++){
String rand=String.valueOf(random.nextInt(10));
sRand+=rand;
g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));
g.drawString(rand,13*i+6,16);
}
// 将认证码存入SESSION
session.setAttribute("code",sRand);
g.dispose();
ImageIO.write(image, "JPEG", response.getOutputStream());
%>
十.WEB-INF/page下的页面:
1.登陆页面login.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!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=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<form action="login" method="post"><br/>
账号:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br/>
验证码:<input type="text" name="code"><img alt="验证码" src="/springmvc-shiro/codeImage.jsp" onclick="changeCode(this)"><br/>
<input type="checkbox" name="rememberMe"/>自动登陆(记住我)<br/>
<input type="submit" value="登陆">
</form>
</body>
</html>
<script type="text/javascript">
function changeCode(o){
o.src="/springmvc-shiro/codeImage.jsp?d="+new Date().getTime();
}
</script>
2.登陆成功的主页面main.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- 使用shiro的jsp标签需要的 -->
<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>系统管理</title>
</head>
<body>
<h3>
<c:if test="${empty user }">
<a href="login">请登录</a>
</c:if>
<c:if test="${not empty user }">欢迎您:${user.nickname }</c:if>
</h3>
<center>
<h1>系统管理</h1>
<!-- shiro的jsp权限判断标签 -->
<!-- 执行到此标签处会到自定义realm中检测权限-->
<shiro:hasPermission name="items:query">
<h4>拥有到商品列表的权限</h4>
</shiro:hasPermission>
<shiro:hasPermission name="items:add">
<h4>拥有添加商品的权限</h4>
</shiro:hasPermission>
<shiro:hasPermission name="items:update">
<h4>拥有修改商品的权限</h4>
</shiro:hasPermission>
<c:forEach items="${user.menus }" var="menu">
<h2><a href="${menu.value }">${menu.key }</a></h2>
</c:forEach>
<!-- jsp的shiro标签 -->
<code>
标签名称 标签条件(均是显示标签内容)
shiro:authenticated 登录之后
shiro:notAuthenticated 不在登录状态时
shiro:guest 用户在没有RememberMe时
shiro:user 用户在RememberMe时
shiro:hasAnyRoles name="abc,123" 在有abc或者123角色时
shiro:hasRole name="abc" 拥有角色abc
shiro:lacksRole name="abc" 没有角色abc
shiro:hasPermission name="abc" 拥有权限资源abc
shiro:lacksPermission name="abc" 没有abc权限资源
shiro:principal 显示用户身份名称
shiro:principal property="username" 显示用户身份中的属性值
</code>
<!-- shiro -->
</center>
<a href="logout">退出登陆</a>
<a href="clearShiroCache">清除shiro缓存</a>
</body>
</html>
3.添加商品的页面addItems.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!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=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<center>
<h1>增加商品页</h1>
<h2><a href="add">确定增加</a></h2>
</center>
</body>
</html>
4.更新商品的页面updateItems.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!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=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<center>
<h1>编辑商品页</h1>
<h2><a href="update">确定编辑</a></h2>
</center>
</body>
</html>
5.商品列表页面itemsList.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!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=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<center>
<h1>商品列表页</h1>
</center>
</body>
</html>