前言
前面一节我们将Shiro+SMM集成Maven项目的MVC基本 “骨架” 构建好了,那么本节就是往里面注入"灵魂",让程序能够顺利跑起来,大家还是继续跟着我的思路走吧~
SMM及Shiro集成配置文件
第十四式:导入配置文件
其实到目前为止,我们的这个 “小项目” 整体框架已经搭差不多啦,但是还剩下最后一个步骤没有做 ,就是配置文件,别项目做着做着忘了配置,结果一运行就哭了,哈哈,把配置放到这里来讲主要是想带着大家走一遍过场,知道我们有什么再来配置,否则真的会一脸懵逼。看完我前面Spring系列博文的朋友这些配置应该不在话下啦
1)application-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">
<context:property-placeholder location="classpath:jdbc.properties" system-properties-mode="FALLBACK"/>
<!-- alibaba Druid数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driverClassName}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
<!-- 设置最大连接数 -->
<property name="maxActive" value="50"></property>
<!-- 设置初始化数量 -->
<property name="initialSize" value="10"></property>
<!-- 设置最大等待时长 -->
<property name="maxWait" value="5000"></property>
<!-- 注入过滤器,关键的一步,开启Druid的监控统计功能 -->
<property name="filters" value="stat"></property>
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="50000" />
<!-- 配置连接的最小生存时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000" />
</bean>
<!-- 声明sessionFactory 并注入mybatis.config.xml(mybatis基本配置文件)-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"></property>
<property name="mapperLocations">
<array>
<value>classpath:mapper/*Mapper.xml</value>
</array>
</property>
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor"></bean>
</array>
</property>
</bean>
<!-- 扫描mapper接口 将sqlSessionFactory注入到mapper中 实现接口和配置文件的关联 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 注入Mapper接口所在的包-->
<property name="basePackage">
<value>
com.marco.mapper
</value>
</property>
<!-- 注入sqlSessionFactory -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
</beans>
2)application-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"
xmlns:tx="http://www.springframework.org/schema/tx"
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/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.marco.service.impl"></context:component-scan>
<!-- 1,声明事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 启动注解事务 -->
<tx:annotation-driven/>
<!-- 2,声明事务的传播特性 也就是通知 -->
<tx:advice id="advice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="edit*" propagation="REQUIRED"/>
<tx:method name="verify*" propagation="REQUIRED"/>
<tx:method name="reset*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="upload*" propagation="REQUIRED"/>
<tx:method name="select*" read-only="true"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="query*" read-only="true"/>
<tx:method name="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 3进行AOP织入 -->
<aop:config>
<aop:pointcut expression="execution(* com.marco.service.impl.*.*(..))" id="point"/>
<aop:advisor advice-ref="advice" pointcut-ref="point"/>
</aop:config>
</beans>
上面两个配置没啥好说的,接下的配置大家就要留意啦,都是和Shiro集成相关的配置,每一个标签的意思我都在标签上注明了
3)application-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"
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">
<!-- 声明凭证匹配器 -->
<bean id="credentialsMatcher"
class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- 注入算法 -->
<property name="hashAlgorithmName" value="md5"></property>
<!-- 注入散列次数 -->
<property name="hashIterations" value="2"></property>
</bean>
<!-- 声明realm -->
<bean id="userRealm" class="com.marco.realms.UserRealm">
<!-- 注入凭证匹配器 -->
<property name="credentialsMatcher" ref="credentialsMatcher"></property>
</bean>
<!-- 创建安全管理器 -->
<bean id="securityManager"
class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- 注入realm -->
<property name="realm" ref="userRealm"></property>
</bean>
<!-- 配置过滤器链 -->
<!-- Shiro 的Web过滤器 id必须和web.xml里面的shiroFilter的 targetBeanName的值一样 -->
<bean id="shiroFilter"
class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,这个属性是必须的 -->
<property name="securityManager" ref="securityManager" />
<!-- 要求登录时的链接(登录页面地址),非必须的属性,默认会自动寻找Web工程根目录下的"/login.jsp"页面 -->
<property name="loginUrl" value="/index.jsp" />
<!-- 登录成功后要跳转的连接(本例中此属性用不到,因为登录成功后的处理逻辑在UserController里硬编码) -->
<!-- <property name="successUrl" value="/success.action"/> -->
<!-- 用户访问未对其授权的资源时,所显示的连接 -->
<property name="unauthorizedUrl" value="/unauthorized.jsp" />
<!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
<property name="filterChainDefinitions">
<value>
<!-- /** = authc 所有url都必须认证通过才可以访问 -->
/index.jsp*=anon
/login/toLogin*=anon
/login/login*=anon
<!-- 如果用户访问user/logout就使用Shiro注销session -->
/login/logout = logout
<!-- /** = anon所有url都不可以匿名访问 -->
<!-- /** = authc -->
<!-- /*/* = authc -->
<!-- /** = authc所有url都不可以匿名访问 必须放到最后面 -->
/** = authc
</value>
</property>
</bean>
</beans>
我们这里选择的算法呢还是MD5算法,加"盐",并散列两次,之前我们是通过shiro.ini文件将用户信息写死,并且将Credential凭证的加密写在Java程序里,但这违背了Spring的IoC/DI的理念,因此我们选择将这些需要new对象的部分配置在xml文件中,这样项目在启动之后会由Spring创建对象并放置在IoC容器中,我们直接去取就可以了。
这里需要特别注意的一点是,因为本次搭建的是Maven的Web项目,因此我们使用的SecurityManager是DefaultWebSecurityManager,不再是之前的DefaultSecurityManager了,注意不要写错了哦。
另外还有一个知识点就是filterChainDefinitions
过滤器链,他是最初步的对url进行筛选的"筛子",类似于/login/logout
的就是访问路径,后面的参数的意思我这边还是以表的形式列举出来。
过滤器名称 | 过滤器类 | 描述 |
---|---|---|
anon | org.apache.shiro.web.filter.authc.AnonymousFilter | 匿名过滤器 |
authc | org.apache.shiro.web.filter.authc.FormAuthenticationFilter | 如果继续操作,需要做对应的表单验证否则不能通过 |
authcBasic | org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter | 基本http验证过滤,如果不通过,跳转屋登录页面 |
logout | org.apache.shiro.web.filter.authc.LogoutFilter | 登录退出过滤器 |
noSessionCreation | org.apache.shiro.web.filter.session.NoSessionCreationFilter | 没有session创建过滤器 |
perms | org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter | 权限过滤器 |
port | org.apache.shiro.web.filter.authz.PortFilter | 端口过滤器,可以设置是否是指定端口如果不是跳转到登录页面 |
rest | org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter | http方法过滤器,可以指定如post不能进行访问等 |
roles | org.apache.shiro.web.filter.authz.RolesAuthorizationFilter | 角色过滤器,判断当前用户是否指定角色 |
ssl | org.apache.shiro.web.filter.authz.SslFilter | 请求需要通过ssl,如果不是跳转回登录页 |
user | org.apache.shiro.web.filter.authc.UserFilter | 如果访问一个已知用户,比如记住我功能,走这个过滤器 |
6)applicationContext.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"
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">
<import resource="classpath:application-dao.xml"/>
<import resource="classpath:application-service.xml"/>
<import resource="classpath:application-shiro.xml"/>
</beans>
5)jdbc.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/shiro
username=root
password=password
6)log4j.properties
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
最后就是我们的web.xml的配置,放在最后当然是压轴的!
7)web.xml
这下面的关于shiro集成的配置一个都不可以少,需要注意的一点是shiroFilter的名字要和application-shiro.xml中的过滤器链的名字保证一致!
因为我们需要过滤的内容是经过DispatcherServelt放行的请求,因此这里在filter-mapping
中配置
<servlet-name>springmvc</servlet-name>
代表你DispatcherServelt放行后的请求都会被本shiroFilter监管!
<?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" id="WebApp_ID" version="3.0">
<display-name>shiro_ssm_layui</display-name>
<!-- shiro集成开始 -->
<!-- shiro过虑器,DelegatingFilterProxy通过代理模式将spring容器中的bean和filter关联起来 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<!-- 设置true代表由servlet容器控制filter的生命周期 -->
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
<!-- 设置spring容器filter的bean id,如果不设置则找与filter-name一致的bean -->
<init-param>
<param-name>targetBeanName</param-name>
<param-value>shiroFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<servlet-name>springmvc</servlet-name>
</filter-mapping>
<!-- shiro集成结束 -->
<!-- 过滤器 -->
<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>
<servlet-name>springmvc</servlet-name>
</filter-mapping>
<filter>
<filter-name>WebStatFilter</filter-name>
<filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
<!-- 注入过滤的文件样式 -->
<init-param>
<!-- 注入表达式,排除一些不必要的url,比如.js,/jslib/等等-->
<param-name>exclusions</param-name>
<param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value>
</init-param>
<!-- 配置profileEnable可监控单个url调用的sql列表 -->
<init-param>
<param-name>profileEnable</param-name>
<param-value>true</param-value>
</init-param>
<!-- cookie监控管理配置 -->
<init-param>
<param-name>principalCookieName</param-name>
<param-value>USER_COOKIE</param-value>
</init-param>
<!-- 默认的sessionStatMaxCount是1000个 -->
<init-param>
<param-name>principalSessionName</param-name>
<param-value>USER_SESSION</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>WebStatFilter</filter-name>
<!-- 过滤的servelt是StatViewServlet -->
<servlet-name>StatViewServlet</servlet-name>
</filter-mapping>
<!-- 监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<servlet>
<!-- 引入StatViewServlet,本质上就是一个servlet-->
<servlet-name>StatViewServlet</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
<!-- 设置账号 -->
<init-param>
<param-name>loginUsername</param-name>
<param-value>root</param-value>
</init-param>
<!-- 设置密码 -->
<init-param>
<param-name>loginPassword</param-name>
<param-value>password</param-value>
</init-param>
<!-- 设置白名单 -->
<init-param>
<param-name>allow</param-name>
<!-- <param-value>192.168.1.1,192.168.2.4</param-value> -->
<param-value></param-value>
</init-param>
<!-- 设置黑名单,注意deny级别高于allow,被deny的地址就算allow了也无法访问 -->
<init-param>
<param-name>deny</param-name>
<param-value></param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>StatViewServlet</servlet-name>
<url-pattern>/druid/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
自定义UserRealm的修改及登录控制器的创建
第十五式:创建LoginController
因为之前在springmvc.xml中已经配置过视图解析器InternalResourceViewResolver
,所以在这里可以直接返回login,代表返回到login.jsp页面,虽然之前在SpringMVC中已经讲到过多次,这里再稍微带一下。
package com.marco.controller;
import javax.servlet.http.HttpSession;
import org.apache.shiro.SecurityUtils;
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 com.marco.utils.ActiveUser;
@Controller
@RequestMapping("login")
public class LoginController {
@RequestMapping("toLogin")
public String toLogin() {
return "login";
}
@RequestMapping("login")
public String login(String username, String userpwd, Model model, HttpSession session) {
//封装token
UsernamePasswordToken token = new UsernamePasswordToken(username, userpwd);
//获取Subject主体
Subject subject = SecurityUtils.getSubject();
// 调用主体的登陆方法
try {
subject.login(token);
System.out.println("登陆成功");
ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
//将当前的user对象存放在session作用域中
session.setAttribute("user", activeUser.getUser());
return "index";
} catch (IncorrectCredentialsException e) {
System.err.println("密码不正确");
model.addAttribute("error", "密码不正确");
} catch (UnknownAccountException e) {
System.err.println("用户名不存在");
model.addAttribute("error", "用户名不存在");
}
return "login";
}
}
第十六式:修改UserRealm
终于到了我们最为关键的一步啦,修改UserRealm,这个之前我们在Shiro的入门篇已经做过类似的案例了,因此理解起来不会那么困难,Realm本质上就是一个安全验证的领域,或者你可以把它理解成一个结界,它就好比一个法师,当你将数据传入,数据首先会被Subject带进SecurityManager的城堡,然后进入到Realm结界,在这个结界中所有的东西都是保密的,SimpleAuthenticationInfo你可以把它当作是一个送口信的,是一个被传递的加密信息,在底层会被结界师凭证匹配器解析,然后返回最终的结果。
package com.marco.realms;
import java.util.List;
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 org.apache.shiro.util.SimpleByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import com.marco.domain.User;
import com.marco.service.PermissionService;
import com.marco.service.RoleService;
import com.marco.service.UserService;
import com.marco.utils.ActiveUser;
public class UserRealm extends AuthorizingRealm{
@Autowired
UserService userService;
@Autowired
RoleService roleService;
@Autowired
PermissionService permissionService;
/**
* 处理授权
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取当前的principal身份信息(一般是用户名、邮箱或者手机号等等)
String principal = (String) token.getPrincipal();
System.out.println("Current principal is " + principal);
//获取当前的credential凭证信息(一般是密码等等)
char[] credentials = (char[]) token.getCredentials();
String credential = new String(credentials);
System.out.println("Current credential is " + credential);
System.out.println("###@#####################################################");
//获取账号信息
String userName = principal.toString();
User user = userService.queryUserByUserName(userName);
if(null != user) {
//查询用户的角色
List<String> roles = roleService.queryRolesByUserId(user.getUserid());
//查询用户的权限
List<String> permissions = permissionService.queryPermissionByUserId(user.getUserid());
//封装activeUser
ActiveUser activeUser = new ActiveUser();
activeUser.setRoles(roles);
activeUser.setUser(user);
activeUser.setPermissions(permissions);
//组装“盐”
String salt = user.getUsername() + user.getAddress();
ByteSource credentialsSalt = new SimpleByteSource(salt.getBytes());
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(activeUser, user.getUserpwd(), credentialsSalt, this.getName());
return authenticationInfo;
}
return null;
}
/**
* 处理认证
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();
List<String> permissions = activeUser.getPermissions();//从activeUser中获取permissions
List<String> roles = activeUser.getRoles();//从activeUser中获取roles
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
if(null != roles && roles.size() > 0) {
authorizationInfo.addRoles(roles);//添加用户角色
}
if(null != permissions && permissions.size() > 0) {
authorizationInfo.addStringPermissions(permissions);//添加用户权限
}
return authorizationInfo;
}
}
前端View层的创建
终于后台的框架已经构建的差不多啦,接下来就是处理前台的,我们这里简化一下为了测试,深思熟虑之后呢还是选择使用"废铁段位"前台页面
第十七式:创建AppListener监听器
知道我套路的朋友因该清楚这个东西的用途了,主要是为了让前台通过${context}指令获取到我们项目的访问名称
package com.marco.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class AppListener implements ServletContextListener{
@Override
public void contextInitialized(ServletContextEvent sce) {
sce.getServletContext().setAttribute("context", sce.getServletContext().getContextPath());
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
第十八式:创建View层页面
先注意看我们的View层的结构,是不是简单到想哭,毕竟我们后台写的这么幸苦…
最外层的index.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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<jsp:forward page="login/toLogin.action"></jsp:forward>
</body>
</html>
第二个页面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=UTF-8">
<title>用户登陆</title>
</head>
<body>
<h1 align="center">用户登陆</h1>
<h5 style="color: red;" align="center">${error}</h5>
<form action="${context}/login/login.action" method="post">
<table align="center" width="50%" border="1" cellpadding="5" cellspacing="5">
<tr>
<td align="right">用户名:</td>
<td>
<input type="text" name="username">
</td>
</tr>
<tr>
<td align="right">密码:</td>
<td>
<input type="password" name="userpwd">
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="提交">
</td>
</tr>
</table>
</form>
</body>
</html>
最后再来个废铁版的index主页,大家不要嫌弃!
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!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>系统首页</title>
</head>
<body>
系统首页
<hr>
<shiro:hasPermission name="user:query">
<a href="${context}/user/query.action">查询用户</a>
<br>
</shiro:hasPermission>
<shiro:hasPermission name="user:add">
<a href="${context}/user/add.action">添加用户</a>
<br>
</shiro:hasPermission>
<shiro:hasPermission name="user:update">
<a href="${context}/user/update.action">修改用户</a>
<br>
</shiro:hasPermission>
<shiro:hasPermission name="user:delete">
<a href="${context}/user/delete.action">删除用户</a>
<br>
</shiro:hasPermission>
<shiro:hasPermission name="user:export">
<a href="${context}/user/export.action">导出用户</a>
<br>
</shiro:hasPermission>
</body>
</html>
欸?大家发现没有,我在每一个功能上面都添加包围了一个标签<shiro:hasPermission name="user:export">
,从字面上翻译hasPermission就是有xxx权限的意思,name里面的内容就是权限的名称,它所代表的意思就是,你有这个权限(例如:user:export),我就把这个按钮给你使用,没有的话,那就bye bye,不给你用。
项目测试
好,整整"降龙十八式"打完!敲的那个累啊… 咱们来测试下吧,运行报错的朋友注意看看配置文件,特别是shiro和SSM集成的application-shiro.xml以及web.xml的配置有没有遗漏或者错误
输入 http://127.0.0.1:8080/marco/index.jsp 进入到一个非常丑的界面之后,我们来试着登录试试看
我的账号密码是marco和123456,我们先来输入一个错误的账户试试看
在输入错误的密码试试看
再输入正确的账号密码发现也没有问题,细心的朋友会发现我们这里少了一个export功能
没错,我在数据库中配置marco的角色是1,而1对应的是超级管理员,而超级管理员我只设置了4个功能,证明我们的安全策略是没有问题的。
后语
到此为止呢,我们项目的初级篇已经完成了…什么?项目还没有完?
没错…接下来就是我们项目改造阶段,这个改造对后期我们学习分布式非常有帮助,所以呢,大家期待一下吧~