创建一个web动态项目
jar包
web.xml
web.xml做了如下几件事情
1. 指定spring的配置文件有两个
1 applicationContext.xml: 用于链接数据库的 2 applicationContext-shiro.xml: 用于配置shiro的
2. 指定springmvc的配置文件
1 springMVC.xml
3. 使用shiro过滤器
1 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns="http://java.sun.com/xml/ns/javaee" 4 xmlns:web="http://java.sun.com/xml/ns/javaee" 5 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> 6 7 <!-- spring的配置文件--> 8 <context-param> 9 <param-name>contextConfigLocation</param-name> 10 <param-value> 11 classpath:applicationContext.xml, 12 classpath:applicationContext-shiro.xml 13 </param-value> 14 </context-param> 15 <listener> 16 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 17 </listener> 18 19 <!-- spring mvc核心:分发servlet --> 20 <servlet> 21 <servlet-name>mvc-dispatcher</servlet-name> 22 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 23 <!-- spring mvc的配置文件 --> 24 <init-param> 25 <param-name>contextConfigLocation</param-name> 26 <param-value>classpath:springMVC.xml</param-value> 27 </init-param> 28 <load-on-startup>1</load-on-startup> 29 </servlet> 30 <servlet-mapping> 31 <servlet-name>mvc-dispatcher</servlet-name> 32 <url-pattern>/</url-pattern> 33 </servlet-mapping> 34 35 <!-- Shiro配置 --> 36 <filter> 37 <filter-name>shiroFilter</filter-name> 38 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 39 <init-param> 40 <param-name>targetFilterLifecycle</param-name> 41 <param-value>true</param-value> 42 </init-param> 43 </filter> 44 <filter-mapping> 45 <filter-name>shiroFilter</filter-name> 46 <url-pattern>/*</url-pattern> 47 </filter-mapping> 48 49 </web-app>
applicationContext.xml
2. 扫描mybatis的mapper什么的,虽然目前还没有提供mapper类
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" 4 xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc" 5 xmlns:context="http://www.springframework.org/schema/context" 6 xmlns:mvc="http://www.springframework.org/schema/mvc" 7 xsi:schemaLocation=" 8 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd 9 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 10 http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd 11 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 12 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 13 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> 14 15 <context:annotation-config /> 16 <context:component-scan base-package="com.how2java.service" /> 17 18 <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> 19 <property name="driverClassName"> 20 <value>com.mysql.jdbc.Driver</value> 21 </property> 22 <property name="url"> 23 <value>jdbc:mysql://localhost:3306/shiro?characterEncoding=UTF-8</value> 24 25 </property> 26 <property name="username"> 27 <value>root</value> 28 </property> 29 <property name="password"> 30 <value>admin</value> 31 </property> 32 </bean> 33 34 <bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean"> 35 <property name="typeAliasesPackage" value="com.how2java.pojo" /> 36 <property name="dataSource" ref="dataSource"/> 37 <property name="mapperLocations" value="classpath:com/how2java/mapper/*.xml"/> 38 </bean> 39 40 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> 41 <property name="basePackage" value="com.how2java.mapper"/> 42 </bean> 43 44 </beans>
applicationContext-shiro.xml
提供shiro的相关配置,简单的说,就是把shiro.ini里的内容搬到这个xml文件里面来了,只是写法不同。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util" 4 xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" 5 xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc" 6 xmlns:aop="http://www.springframework.org/schema/aop" 7 xsi:schemaLocation="http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/tx 9 http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context 10 http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/mvc 11 http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop 12 http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/util 13 http://www.springframework.org/schema/util/spring-util.xsd"> 14 15 <!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 --> 16 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 17 <!-- 调用我们配置的权限管理器 --> 18 <property name="securityManager" ref="securityManager" /> 19 <!-- 配置我们的登录请求地址 --> 20 <property name="loginUrl" value="/login" /> 21 <!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 --> 22 <property name="unauthorizedUrl" value="/unauthorized" /> 23 <!-- 退出 --> 24 <property name="filters"> 25 <util:map> 26 <entry key="logout" value-ref="logoutFilter" /> 27 </util:map> 28 </property> 29 <!-- 权限配置 --> 30 <property name="filterChainDefinitions"> 31 <value> 32 <!-- anon表示此地址不需要任何权限即可访问 --> 33 /login=anon 34 /index=anon 35 /static/**=anon 36 /doLogout=logout 37 <!--所有的请求(除去配置的静态资源请求或请求地址为anon的请求)都要通过登录验证,如果未登录则跳到/login --> 38 /** = authc 39 </value> 40 </property> 41 </bean> 42 <!-- 退出过滤器 --> 43 <bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter"> 44 <property name="redirectUrl" value="/index" /> 45 </bean> 46 47 <!-- 会话ID生成器 --> 48 <bean id="sessionIdGenerator" 49 class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator" /> 50 <!-- 会话Cookie模板 关闭浏览器立即失效 --> 51 <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> 52 <constructor-arg value="sid" /> 53 <property name="httpOnly" value="true" /> 54 <property name="maxAge" value="-1" /> 55 </bean> 56 <!-- 会话DAO --> 57 <bean id="sessionDAO" 58 class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO"> 59 <property name="sessionIdGenerator" ref="sessionIdGenerator" /> 60 </bean> 61 <!-- 会话验证调度器,每30分钟执行一次验证 ,设定会话超时及保存 --> 62 <bean name="sessionValidationScheduler" 63 class="org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler"> 64 <property name="interval" value="1800000" /> 65 <property name="sessionManager" ref="sessionManager" /> 66 </bean> 67 <!-- 会话管理器 --> 68 <bean id="sessionManager" 69 class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> 70 <!-- 全局会话超时时间(单位毫秒),默认30分钟 --> 71 <property name="globalSessionTimeout" value="1800000" /> 72 <property name="deleteInvalidSessions" value="true" /> 73 <property name="sessionValidationSchedulerEnabled" value="true" /> 74 <property name="sessionValidationScheduler" ref="sessionValidationScheduler" /> 75 <property name="sessionDAO" ref="sessionDAO" /> 76 <property name="sessionIdCookieEnabled" value="true" /> 77 <property name="sessionIdCookie" ref="sessionIdCookie" /> 78 </bean> 79 80 <!-- 安全管理器 --> 81 <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 82 <property name="realm" ref="databaseRealm" /> 83 <property name="sessionManager" ref="sessionManager" /> 84 </bean> 85 <!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) --> 86 <bean 87 class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> 88 <property name="staticMethod" 89 value="org.apache.shiro.SecurityUtils.setSecurityManager" /> 90 <property name="arguments" ref="securityManager" /> 91 </bean> 92 93 <bean id="databaseRealm" class="com.how2java.realm.DatabaseRealm"> 94 </bean> 95 96 <!-- 保证实现了Shiro内部lifecycle函数的bean执行 --> 97 <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> 98 </beans>
springMVC.xml
1. springmvc的基本配置
2. 增加了对shiro的支持
这样可以在控制器Controller上,使用像@RequireRole 这样的注解,来表示某个方法必须有相关的角色才能访问
3. 指定了异常处理类DefaultExceptionHandler,这样当访问没有权限的资源的时候,就会跳到统一的页面去显示错误信息
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" 4 xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc" 5 xmlns:context="http://www.springframework.org/schema/context" 6 xmlns:mvc="http://www.springframework.org/schema/mvc" 7 xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd 8 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 9 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 10 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd 11 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 12 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"> 13 14 <context:annotation-config/> 15 16 <context:component-scan base-package="com.how2java.controller"> 17 <context:include-filter type="annotation" 18 expression="org.springframework.stereotype.Controller"/> 19 </context:component-scan> 20 21 <mvc:annotation-driven /> 22 23 <mvc:default-servlet-handler /> 24 25 <bean 26 class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 27 <property name="viewClass" 28 value="org.springframework.web.servlet.view.JstlView" /> 29 <property name="prefix" value="/WEB-INF/jsp/" /> 30 <property name="suffix" value=".jsp" /> 31 </bean> 32 33 <!--启用shiro注解 --> 34 <bean 35 class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" 36 depends-on="lifecycleBeanPostProcessor"> 37 <property name="proxyTargetClass" value="true" /> 38 </bean> 39 <bean 40 class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> 41 <property name="securityManager" ref="securityManager" /> 42 </bean> 43 44 <!-- 控制器异常处理 --> 45 <bean id="exceptionHandlerExceptionResolver" class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver"> 46 </bean> 47 <bean class="com.how2java.exception.DefaultExceptionHandler"/> 48 49 </beans>
log4j.properties
1 # Global logging configuration 2 log4j.rootLogger=ERROR, stdout 3 # MyBatis logging configuration... 4 log4j.logger.com.how2java=TRACE 5 # Console output... 6 log4j.appender.stdout=org.apache.log4j.ConsoleAppender 7 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 8 log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
PageController
因为使用 ssm,所以jsp通常都会放在WEB-INF/jsp 下面,而这个位置是无法通过浏览器直接访问的,所以就会专门做这么一个类,便于访问这些jsp。
比如要访问WEB-INF/jsp/index.jsp文件,那么就通过/index 这个路径来访问。
这个类还有两点需要注意:
1. /login 只支持get方式。 post方式是后续用来进行登录行为的,这里的get方式仅仅用于显示登录页面
2. 权限注解:
通过注解: @RequiresRoles("admin") 指明了 访问 deleteProduct 需要角色"admin"
通过注解:@RequiresPermissions("deleteOrder") 指明了 访问 deleteOrder 需要权限"deleteOrder"
1 package com.how2java.controller; 2 3 import org.apache.shiro.authz.annotation.RequiresPermissions; 4 import org.apache.shiro.authz.annotation.RequiresRoles; 5 import org.springframework.stereotype.Controller; 6 import org.springframework.web.bind.annotation.RequestMapping; 7 import org.springframework.web.bind.annotation.RequestMethod; 8 9 //专门用于显示页面的控制器 10 @Controller 11 @RequestMapping("") 12 public class PageController { 13 14 @RequestMapping("index") 15 public String index(){ 16 return "index"; 17 } 18 19 @RequiresPermissions("deleteOrder") 20 @RequestMapping("deleteOrder") 21 public String deleteOrder(){ 22 return "deleteOrder"; 23 } 24 @RequiresRoles("admin") 25 @RequestMapping("deleteProduct") 26 public String deleteProduct(){ 27 return "deleteProduct"; 28 } 29 @RequestMapping("listProduct") 30 public String listProduct(){ 31 return "listProduct"; 32 } 33 34 @RequestMapping(value="/login",method=RequestMethod.GET) 35 public String login(){ 36 return "login"; 37 } 38 @RequestMapping("unauthorized") 39 public String noPerms(){ 40 return "unauthorized"; 41 } 42 43 }
index.jsp
index页面
1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8"%> 3 <html> 4 <head> 5 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 6 7 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 8 9 <link rel="stylesheet" type="text/css" href="static/css/style.css" /> 10 11 </head> 12 <body> 13 14 <div class="workingroom"> 15 <div class="loginDiv"> 16 17 <c:if test="${empty subject.principal}"> 18 <a href="login">登录</a><br> 19 </c:if> 20 <c:if test="${!empty subject.principal}"> 21 <span class="desc">你好,${subject.principal},</span> 22 <a href="doLogout">退出</a><br> 23 </c:if> 24 25 <a href="listProduct">查看产品</a><span class="desc">(登录后才可以查看) </span><br> 26 <a href="deleteProduct">删除产品</a><span class="desc">(要有产品管理员角色, zhang3没有,li4 有) </span><br> 27 <a href="deleteOrder">删除订单</a><span class="desc">(要有删除订单权限, zhang3有,li4没有) </span><br> 28 </div> 29 30 </body> 31 </html>
listProduct.jsp
需要登录才能访问的页面
1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8" import="java.util.*"%> 3 4 <!DOCTYPE html> 5 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 7 <link rel="stylesheet" type="text/css" href="static/css/style.css" /> 8 9 <div class="workingroom"> 10 11 listProduct.jsp ,能进来,就表示已经登录成功了 12 <br> 13 <a href="#" onClick="javascript:history.back()">返回</a> 14 </div>
deleteProduct.jsp
需要角色才能访问的页面
1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8" import="java.util.*"%> 3 4 <!DOCTYPE html> 5 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 7 <link rel="stylesheet" type="text/css" href="static/css/style.css" /> 8 9 <div class="workingroom"> 10 11 deleteProduct.jsp,能进来<br>就表示拥有 productManager 角色 12 <br> 13 <a href="#" onClick="javascript:history.back()">返回</a> 14 </div>
deleteOrder.jsp
需要权限deleteOrder 才能访问的页面
1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8" import="java.util.*"%> 3 4 <!DOCTYPE html> 5 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 7 <link rel="stylesheet" type="text/css" href="static/css/style.css" /> 8 9 <div class="workingroom"> 10 11 deleteOrder.jsp ,能进来,就表示有deleteOrder权限 12 <br> 13 <a href="#" onClick="javascript:history.back()">返回</a> 14 </div>
login.jsp
登陆页面
1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8" import="java.util.*"%> 3 4 <!DOCTYPE html> 5 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 7 <link rel="stylesheet" type="text/css" href="static/css/style.css" /> 8 9 <div class="workingroom"> 10 11 <div class="errorInfo">${error}</div> 12 <form action="login" method="post"> 13 账号: <input type="text" name="name"> <br> 14 密码: <input type="password" name="password"> <br> 15 <br> 16 <input type="submit" value="登录"> 17 <br> 18 <br> 19 <div> 20 <span class="desc">账号:zhang3 密码:12345 角色:admin</span><br> 21 <span class="desc">账号:li4 密码:abcde 角色:productManager</span><br> 22 </div> 23 24 </form> 25 </div>
unauthorized.jsp
没有角色,没有权限都会跳转到这个页面来
1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8" import="java.util.*"%> 3 4 <!DOCTYPE html> 5 6 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 7 <link rel="stylesheet" type="text/css" href="static/css/style.css" /> 8 9 <div class="workingroom"> 10 11 权限不足,具体原因:${ex.message} 12 <br> 13 <a href="#" onClick="javascript:history.back()">返回</a> 14 </div>
style.css
样式文件
1 span.desc{ 2 margin-left:20px; 3 color:gray; 4 } 5 div.workingroom{ 6 margin:200px auto; 7 width:400px; 8 } 9 div.workingroom a{ 10 display:inline-block; 11 margin-top:20px; 12 } 13 div.loginDiv{ 14 text-align: left; 15 } 16 div.errorInfo{ 17 color:red; 18 font-size:0.65em; 19 }
User 一套
一套是指实体类,Mapper,xml文件, Service接口,Service实现类
需要说明的是UserMapper是得到一个User对象,但是在UserService里又返回的是这个对象的password属性,这样做是为了和前面教程里DAO 里的做法保持一直
1.User.java
1 package com.how2java.pojo; 2 3 public class User { 4 5 private int id; 6 private String name; 7 private String password; 8 public String getName() { 9 return name; 10 } 11 public void setName(String name) { 12 this.name = name; 13 } 14 public String getPassword() { 15 return password; 16 } 17 public void setPassword(String password) { 18 this.password = password; 19 } 20 public int getId() { 21 return id; 22 } 23 public void setId(int id) { 24 this.id = id; 25 } 26 27 }
2.UserMapper.java
1 package com.how2java.mapper; 2 3 import com.how2java.pojo.User; 4 5 public interface UserMapper { 6 7 public User getByName(String name); 8 }
3.User.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 6 <mapper namespace="com.how2java.mapper.UserMapper"> 7 <select id="getByName" parameterType="string" resultType="User"> 8 select * from user where name = #{0} 9 </select> 10 </mapper>
4.UserService.java
1 package com.how2java.service; 2 3 public interface UserService { 4 public String getPassword(String name); 5 }
5.UserServiceImpl.java
1 package com.how2java.service.impl; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.stereotype.Service; 5 6 import com.how2java.mapper.UserMapper; 7 import com.how2java.pojo.User; 8 import com.how2java.service.UserService; 9 10 @Service 11 public class UserServiceImpl implements UserService{ 12 13 @Autowired UserMapper userMapper; 14 15 @Override 16 public String getPassword(String name) { 17 // TODO Auto-generated method stub 18 User u = userMapper.getByName(name); 19 if(null==u) 20 return null; 21 return u.getPassword(); 22 } 23 24 }
Role 一套
Role 一套,与User 一套类似的,RoleMapper返回的是Role的集合,而RoleService里返回的就是String 的集合了
1.Role.java
1 package com.how2java.pojo; 2 3 public class Role { 4 private int id; 5 private String name; 6 public int getId() { 7 return id; 8 } 9 public void setId(int id) { 10 this.id = id; 11 } 12 public String getName() { 13 return name; 14 } 15 public void setName(String name) { 16 this.name = name; 17 } 18 @Override 19 public String toString() { 20 return "Role [id=" + id + ", name=" + name + "]"; 21 } 22 23 }
2.RoleMapper.java
1 package com.how2java.mapper; 2 3 import java.util.List; 4 5 import com.how2java.pojo.Role; 6 7 public interface RoleMapper { 8 public List<Role> listRolesByUserName(String userName); 9 10 }
3.Role.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 6 <mapper namespace="com.how2java.mapper.RoleMapper"> 7 <select id="listRolesByUserName" parameterType="string" resultType="Role"> 8 select r.id, r.name from user u 9 left join user_role ur 10 on u.id = ur.uid 11 left join Role r 12 on r.id = ur.rid 13 where u.name = #{0} 14 </select> 15 </mapper>
4.RoleService.java
1 package com.how2java.service; 2 3 import java.util.Set; 4 5 public interface RoleService { 6 public Set<String> listRoles(String userName); 7 }
5.RoleServiceImpl
1 package com.how2java.service.impl; 2 3 import java.util.HashSet; 4 import java.util.List; 5 import java.util.Set; 6 7 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.stereotype.Service; 9 10 import com.how2java.mapper.RoleMapper; 11 import com.how2java.pojo.Role; 12 import com.how2java.service.RoleService; 13 14 @Service 15 public class RoleServiceImpl implements RoleService{ 16 17 @Autowired RoleMapper roleMapper; 18 19 @Override 20 public Set<String> listRoles(String userName) { 21 List<Role> roles = roleMapper.listRolesByUserName(userName); 22 Set<String> result = new HashSet<>(); 23 for (Role role: roles) { 24 result.add(role.getName()); 25 } 26 return result; 27 } 28 }
Permission 一套
Permission 一套,与User 一套类似的,PermissionMapper返回的是 Permission 的集合,而 PermissionService 里返回的就是String 的集合了
1.Permission.java
1 package com.how2java.pojo; 2 3 public class Permission { 4 private int id; 5 private String name; 6 public int getId() { 7 return id; 8 } 9 public void setId(int id) { 10 this.id = id; 11 } 12 public String getName() { 13 return name; 14 } 15 public void setName(String name) { 16 this.name = name; 17 } 18 @Override 19 public String toString() { 20 return "Permission [id=" + id + ", name=" + name + "]"; 21 } 22 23 }
2.PermissionMapper.java
1 package com.how2java.mapper; 2 3 import java.util.List; 4 5 import com.how2java.pojo.Permission; 6 7 public interface PermissionMapper { 8 public List<Permission> listPermissionsByUserName(String userName); 9 10 }
3.Permission.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 6 <mapper namespace="com.how2java.mapper.PermissionMapper"> 7 <select id="listPermissionsByUserName" parameterType="string" resultType="Permission"> 8 select p.id, p.name from user u 9 left join user_role ru 10 on u.id = ru.uid left 11 join role r 12 on r.id = ru.rid 13 left join role_permission rp 14 on r.id = rp.rid 15 left join permission p 16 on p.id = rp.pid 17 where u.name =#{0} 18 </select> 19 </mapper>
4.PermissionService.java
1 package com.how2java.service; 2 3 import java.util.Set; 4 5 public interface PermissionService { 6 public Set<String> listPermissions(String userName); 7 }
5.PermissionServiceImpl.java
1 package com.how2java.service.impl; 2 3 import java.util.HashSet; 4 import java.util.List; 5 import java.util.Set; 6 7 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.stereotype.Service; 9 10 import com.how2java.mapper.PermissionMapper; 11 import com.how2java.pojo.Permission; 12 import com.how2java.service.PermissionService; 13 14 @Service 15 public class PermissionServiceImpl implements PermissionService{ 16 17 @Autowired PermissionMapper permissionMapper; 18 19 @Override 20 public Set<String> listPermissions(String userName) { 21 List<Permission> permissions = permissionMapper.listPermissionsByUserName(userName); 22 Set<String> result = new HashSet<>(); 23 for (Permission permission: permissions) { 24 result.add(permission.getName()); 25 } 26 return result; 27 } 28 }
LoginController
进行登录的控制器,和 LoginServlet 一样,获取账号密码进行验证,如果成功了就客户端跳转到index,否则就返回login.jsp页面。
需要注意的是,这里用的是 post 方式
@RequestMapping(value="/login",method=RequestMethod.POST)
区别于PageController里用 get 方式的 login
1 package com.how2java.controller; 2 3 import org.apache.shiro.SecurityUtils; 4 import org.apache.shiro.authc.AuthenticationException; 5 import org.apache.shiro.authc.UsernamePasswordToken; 6 import org.apache.shiro.session.Session; 7 import org.apache.shiro.subject.Subject; 8 import org.springframework.stereotype.Controller; 9 import org.springframework.ui.Model; 10 import org.springframework.web.bind.annotation.RequestMapping; 11 import org.springframework.web.bind.annotation.RequestMethod; 12 13 @Controller 14 @RequestMapping("") 15 public class LoginController { 16 @RequestMapping(value="/login",method=RequestMethod.POST) 17 public String login(Model model,String name, String password) { 18 Subject subject = SecurityUtils.getSubject(); 19 UsernamePasswordToken token = new UsernamePasswordToken(name, password); 20 try { 21 subject.login(token); 22 Session session=subject.getSession(); 23 session.setAttribute("subject", subject); 24 return "redirect:index"; 25 26 } catch (AuthenticationException e) { 27 model.addAttribute("error", "验证失败"); 28 return "login"; 29 } 30 } 31 32 }
DatabaseRealm
这里才是真正做登录验证和授权的地方
做法和 数据库支持 教程中的DatabaseRealm 做法一模一样,区别只是在于把DAO 换成了 Service
而这个 DatabaseRealm 的使用,是声明在 applicationContext-shiro.xml 中的:
<bean id="databaseRealm" class="com.how2java.realm.DatabaseRealm"></bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"><property name="realm" ref="databaseRealm" /><property name="sessionManager" ref="sessionManager" /></bean>
1 package com.how2java.realm; 2 3 import java.util.Set; 4 5 import org.apache.shiro.authc.AuthenticationException; 6 import org.apache.shiro.authc.AuthenticationInfo; 7 import org.apache.shiro.authc.AuthenticationToken; 8 import org.apache.shiro.authc.SimpleAuthenticationInfo; 9 import org.apache.shiro.authc.UsernamePasswordToken; 10 import org.apache.shiro.authz.AuthorizationInfo; 11 import org.apache.shiro.authz.SimpleAuthorizationInfo; 12 import org.apache.shiro.realm.AuthorizingRealm; 13 import org.apache.shiro.subject.PrincipalCollection; 14 import org.springframework.beans.factory.annotation.Autowired; 15 16 import com.how2java.service.PermissionService; 17 import com.how2java.service.RoleService; 18 import com.how2java.service.UserService; 19 20 public class DatabaseRealm extends AuthorizingRealm { 21 22 @Autowired 23 private UserService userService; 24 @Autowired 25 private RoleService roleService; 26 @Autowired 27 private PermissionService permissionService; 28 29 @Override 30 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { 31 //能进入到这里,表示账号已经通过验证了 32 String userName =(String) principalCollection.getPrimaryPrincipal(); 33 //通过service获取角色和权限 34 Set<String> permissions = permissionService.listPermissions(userName); 35 Set<String> roles = roleService.listRoles(userName); 36 37 //授权对象 38 SimpleAuthorizationInfo s = new SimpleAuthorizationInfo(); 39 //把通过service获取到的角色和权限放进去 40 s.setStringPermissions(permissions); 41 s.setRoles(roles); 42 return s; 43 } 44 45 @Override 46 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 47 //获取账号密码 48 UsernamePasswordToken t = (UsernamePasswordToken) token; 49 String userName= token.getPrincipal().toString(); 50 String password= new String( t.getPassword()); 51 //获取数据库中的密码 52 String passwordInDB = userService.getPassword(userName); 53 54 //如果为空就是账号不存在,如果不相同就是密码错误,但是都抛出AuthenticationException,而不是抛出具体错误原因,免得给破解者提供帮助信息 55 if(null==passwordInDB || !passwordInDB.equals(password)) 56 throw new AuthenticationException(); 57 58 //认证信息里存放账号密码, getName() 是当前Realm的继承方法,通常返回当前类名 :databaseRealm 59 SimpleAuthenticationInfo a = new SimpleAuthenticationInfo(userName,password,getName()); 60 return a; 61 } 62 63 }
DefaultExceptionHandler
最后是异常处理,当发生UnauthorizedException 异常的时候,就表示访问了无授权的资源,那么就会跳转到unauthorized.jsp,而在unauthorized.jsp 中就会把错误信息通过变量 ex 取出来。
DefaultExceptionHandler 的使用,是声明在 springMVC.xml 的最后几行:
<bean id="exceptionHandlerExceptionResolver" class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver"></bean><bean class="com.how2java.exception.DefaultExceptionHandler"/>
1 package com.how2java.exception; 2 3 import org.apache.shiro.authz.UnauthorizedException; 4 import org.springframework.http.HttpStatus; 5 import org.springframework.web.bind.annotation.ControllerAdvice; 6 import org.springframework.web.bind.annotation.ExceptionHandler; 7 import org.springframework.web.bind.annotation.ResponseStatus; 8 import org.springframework.web.context.request.NativeWebRequest; 9 import org.springframework.web.servlet.ModelAndView; 10 11 @ControllerAdvice 12 public class DefaultExceptionHandler { 13 @ExceptionHandler({UnauthorizedException.class}) 14 @ResponseStatus(HttpStatus.UNAUTHORIZED) 15 public ModelAndView processUnauthenticatedException(NativeWebRequest request, UnauthorizedException e) { 16 ModelAndView mv = new ModelAndView(); 17 mv.addObject("ex", e); 18 mv.setViewName("unauthorized"); 19 return mv; 20 } 21 }