以下是基于spring和Shiro的整合,此篇要点分为两部分:新增用户时,使用MD5和盐加密用户密码;使用shiro认证用户两部分。由于该小项目是完成后总结的,步骤和正常开发可能有些出入,还有该项目异常部分应用了日志记录,具体的日志配置可参考我上篇文章。
思路:
账户、密码认证:
1、创建Subject主体;
2、将从前端得到的账号,密码存放到Token中;
3、再使用subject.login(token)提交认证;
4、UserRealm的doGetAuthenticationInfo认证方法中,通过从token中拿到账户号,从数据查询出用户的密码和盐;new 一个SimpleAuthenticationInfo ,将账户名、密码、盐(使用ByteSource.Util.bytes()方法转换为ByteSource类型)、当前Realm的name封装进去。
4、返回SimpleAuthenticationInfo对象(该对象会传入凭证匹配器中),凭证匹配器HashedCredentialsMatcher 会根据Token(你前台登录时的明文账号密码,和数据库查询出来的加密后的密码比较)
实现步骤:
(一)准备工作(基于SSM框架创建项目):
引入maven依赖:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<spring.version>5.1.8.RELEASE</spring.version>
<jackson.version>2.9.8</jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.1</version>
</dependency>
<!--数据库连接驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!--数据连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</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>
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
</dependencies>
login.jsp(登录页面)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录界面</title>
</head>
<body>
<form action="toHome" method="post">
账号:<input name="userIdCode" type="text"/>${userName}
<br/>
密码:<input name="password" type="password"/>${password}
<br/>
<input type="submit" value="登录"/>
</form>
</body>
</html>
home.jsp(主页面)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>主页面</title>
</head>
<body>
<h2>登录成功!</h2>
<button onclick="window.location.href='toAddUser'">新增用户</button>
</body>
</html>
addUser.jsp(新增用户界面)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>新增用户</title>
</head>
<body>
<form action="addUser" method="post">
姓名:<input name="name" type="text"><br/>
密码:<input name="password" type="password"><br/>
年龄:<input name="age" type="number"><br/>
性别:<input name="sex" type="radio" value="男" checked>男<input name="sex" type="radio" value="女">女<br/>
<button type="submit">登录</button>
</form>
</body>
</html>
showIdCode.jsp(新增成功后的反馈界面)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>当前用户的员工编号</title>
</head>
<body>
<h2>新增员工成功!新增员工编号为${IdCode}!</h2>
<br/>
<button onclick="window.location.href='login'">
确定
</button>
</body>
</html>
接着是数据库表结构:
实体类User
package com.xcj.jquery_ajax.entity;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class User {
//用户编号(账号,自增)
private Integer idCode;
private String password;
private String name;
private int age;
private String sex;
//盐
private String salt;
}
Dao层(UserDao):
package com.xcj.jquery_ajax.dao;
import com.xcj.jquery_ajax.entity.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository("UserDao")
public interface UserDao {
public User findUserByIdCode(@Param("idCode") Integer id_code);
public void addUser(User user);
}
UserDao.xml
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.4//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xcj.jquery_ajax.dao.UserDao">
<resultMap id="resultMap" type="User">
<id property="idCode" column="id_code" javaType="Integer"/>
<result property="password" column="password" javaType="String"/>
<result property="name" column="name" javaType="String"/>
<result property="age" column="age" javaType="Integer"/>
<result property="sex" column="sex" javaType="String"/>
<result property="salt" column="salt" javaType="String"/>
</resultMap>
<select id="findUserByIdCode" parameterType="Integer" resultMap="resultMap">
SELECT * FROM user WHERE id_code = #{idCode}
</select>
<insert id="addUser" parameterType="User" useGeneratedKeys="true" keyProperty="idCode" keyColumn="idCode">
INSERT INTO user (password,name,age,sex,salt) VALUES (#{password},#{name},#{age},#{sex},#{salt})
</insert>
</mapper>
UserService
package com.xcj.jquery_ajax.service;
import com.xcj.jquery_ajax.entity.User;
import java.util.List;
public interface UserService {
/*根据员工编号查询用户数据*/
public User findUserByIdCode(Integer idCode);
/*新增员工*/
public void addUser(User user);
}
UserServiceImp
package com.xcj.jquery_ajax.service.serviceImp;
import com.xcj.jquery_ajax.dao.UserDao;
import com.xcj.jquery_ajax.entity.User;
import com.xcj.jquery_ajax.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("UserService")
public class UserServiceImp implements UserService {
@Autowired
UserDao userDao;
@Override
public User findUserByIdCode(Integer idCode) {
return userDao.findUserByIdCode(idCode);
}
@Override
public void addUser(User user) {
userDao.addUser(user);
}
}
UserController,
新建用户步骤:
1、新建User对象,设值(自定义随机数生成工具类MyRandomUtil,生成盐;使用SimpleHash类对密码进行加密)
2、调用UserService类新增用户方法。
shiro认证的步骤:
1、创建Subject主体;
2、将从前端得到的账号,密码存放到Token中;
3、再使用subject.login(token)提交认证,可能会发生异常,这里我只处理了两个异常。
package com.xcj.jquery_ajax.controller;
import com.xcj.jquery_ajax.entity.User;
import com.xcj.jquery_ajax.service.UserService;
import com.xcj.jquery_ajax.tool.MyRandomUtil;
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.crypto.hash.SimpleHash;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller("UserController")
public class UserController {
@Autowired
UserService userService;
//跳转登录界面
@RequestMapping("login")
public ModelAndView login() {
return new ModelAndView("login");
}
//shiro登录认证流程
@RequestMapping("toHome")
public ModelAndView toHome(String userIdCode , String password){
ModelAndView modelAndView = new ModelAndView();
//通过shiro的一个工具类SecurityUtil获取主体subject
Subject subject = SecurityUtils.getSubject();
//UsernamePasswordToken用于实现基于用户名/密码主体(Subject)身份认证。
//UsernamePasswordToken实现了 RememberMeAuthenticationToken 和HostAuthenticationToken,可以实现“记住我”及“主机验证”的支持。
UsernamePasswordToken token = new UsernamePasswordToken(userIdCode, password);
try {
//当调用subject的登入方法时,会跳转到认证的方法上。
subject.login(token);
} catch (UnknownAccountException e) {
e.printStackTrace();
modelAndView.addObject("userName", "用户id错误!");
modelAndView.setViewName("login");
return modelAndView;
} catch (IncorrectCredentialsException e) {
e.printStackTrace();
modelAndView.addObject("password", "用户密码错误!");
modelAndView.setViewName("login");
return modelAndView;
}
modelAndView.setViewName("home");
return modelAndView;
}
//跳转到新增用户界面
@RequestMapping("toAddUser")
public ModelAndView toAddUser(){
return new ModelAndView("addUser");
}
//新增用户处理
@RequestMapping(value = "addUser",method = RequestMethod.POST)
public ModelAndView addUser(String name,Integer age,String sex,String password){
User user = new User();
user.setName(name);
user.setAge(age);
user.setSex(sex);
//自定义随机数生成工具类,生成盐
String salt = MyRandomUtil.getRandom();
user.setSalt(salt);
//使用SimpleHash类对密码进行加密
String pwd = new SimpleHash("MD5",password, ByteSource.Util.bytes(salt),1).toHex();
user.setPassword(pwd);
userService.addUser(user);
//idCode的值由MyBatis的useGeneratedKeys属性返回
Integer idCode = user.getIdCode();
return new ModelAndView("showIdCode").addObject("IdCode",idCode);
}
}
随机数生成工具类MyRandomUtil
package com.xcj.jquery_ajax.tool;
import java.util.Random;
public class MyRandomUtil {
public static String getRandom() {
String str = "";
char[] ch = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
Random random = new Random();
for (int i = 0; i < 8; i++) {
char num = ch[random.nextInt(ch.length)];
str += num;
}
return str;
}
}
UserRealm(自定义Realm),暂时只涉及认证,授权过程将在后续更新。
认证过程:
1、从token中拿到username,即用户账号(这是我们在controller层传进去的 idCode)。
2、通过username从数据库获取User对象,获取对应密码(数据库存放的密码已经过加密处理)和salt(盐)
3、new 一个SimpleAuthenticationInfo ,将账户名、密码、盐(使用ByteSource.Util.bytes()方法转换为ByteSource类型)、当前Realm的name封装进去。
4、返回SimpleAuthenticationInfo对象(该对象会传入凭证匹配器中)。
package com.xcj.jquery_ajax.realm;
import com.xcj.jquery_ajax.entity.User;
import com.xcj.jquery_ajax.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
/*授权*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
/*认证*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
User user = null;
if (username != null && !username.equals("")) {
//通过token中获取到的账号(username)从数据库中拿到user对象
user = userService.findUserByIdCode(Integer.valueOf(username));
}
if (user != null) {
String password = user.getPassword();
String salt = user.getSalt();
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password, ByteSource.Util.bytes(salt), this.getName());
return simpleAuthenticationInfo;
}
return null;
}
}
applicationContext.xml中,我们将UserRealm的凭证匹配器改为了HashedCredentialsMatcher。它会解析SimpleAuthenticationInfo,帮我们比较账户名、密码是否一致,如果不一致将返回对应Exception。
<?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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
<!--开启springmvc注解-->
<!--自动扫描包下带注解的类,加载为bean-->
<context:component-scan base-package="com.xcj.jquery_ajax"/>
<mvc:annotation-driven/>
<!--spring中导入properties文件-->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>classpath:applicationContext.properties</value>
</property>
</bean>
<!--使用Druid数据库连接池管理监控数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${spring.datasource.driver}"/>
<property name="url" value="${spring.datasource.url}"/>
<property name="username" value="${spring.datasource.username}"/>
<property name="password" value="${spring.datasource.password}"/>
<property name="initialSize" value="${spring.datasource.initialSize}"/>
<property name="minIdle" value="${spring.datasource.minIdle}"/>
<property name="maxActive" value="${spring.datasource.maxActive}"/>
<property name="maxWait" value="${spring.datasource.maxWait}"/>
<property name="timeBetweenEvictionRunsMillis" value="${spring.datasource.timeBetweenEvictionRunsMillis}"/>
<property name="minEvictableIdleTimeMillis" value="${spring.datasource.minEvictableIdleTimeMillis}"/>
<property name="testWhileIdle" value="${spring.datasource.testWhileIdle}"/>
<property name="testOnBorrow" value="${spring.datasource.testOnBorrow}"/>
<property name="testOnReturn" value="${spring.datasource.testOnReturn}"/>
<property name="filters" value="${spring.datasource.filters}"/>
<property name="connectionProperties" value="${spring.datasource.connectionProperties}"/>
<property name="useGlobalDataSourceStat" value="${spring.datasource.useGlobalDataSourceStat}"/>
</bean>
<!--spring和mybatis整合,使用SqlSessionFactoryBean管理数据源等-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="com.xcj.jquery_ajax.entity"/>
</bean>
<!--MapperScannerConfigurer会查找类路径下的映射器并自动将它们创建成MapperFactoryBeans,相当于把Dao层托管给spring,需要用时直接注入-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.xcj.jquery_ajax.dao"/>
</bean>
<!--配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 返回视图页面的前缀 -->
<property name="prefix" value="/WEB-INF/view/"></property>
<!-- 返回页面的后缀 -->
<property name="suffix" value=".jsp"></property>
</bean>
<!--自定义spring的全局异常处理,实现HandlerExceptionResolver接口-->
<bean class="com.xcj.jquery_ajax.execption.MyExceptionResolver"/>
<!--Spring MVC的web.xml文件中关于DispatcherServlet拦截url的配置为"/",拦截了所有的请求,同时*.js,*.jpg等静态资源也被拦截了,
导致运行时跳转后的页面无法加载图片资源,所以需要配置该静态资源-->
<mvc:resources mapping="css/**" location="css/"/>
<mvc:resources mapping="js/**" location="js/"/>
<!--shiro拦截器的bean-->
<bean name="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!--设置登入页面-->
<property name="loginUrl" value="/login"/>
<!--设置登录成功的跳转页面-->
<property name="successUrl" value="/toHome"/>
<!--设置没有权限跳转的页面-->
<!-- <property name="unauthorizedUrl" value="/unauthorized"/>-->
<!--自定义拦截器-->
<property name="filters">
<map>
<!--退出拦截器-->
<entry key="logout" value-ref="logoutFilter"/>
</map>
</property>
<!--拦截器链,顺序执行-->
<property name="filterChainDefinitions">
<value>
/login = anon <!--登录界面不拦截-->
/toHome = anon
/logout = logout<!--退出-->
/** = authc<!--必须登录认证成功才能访问-->
</value>
</property>
</bean>
<!--shiro提供的退出拦截器,redirectUrl对应退出后跳转的地址-->
<bean name="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter">
<property name="redirectUrl" value="/login"/>
</bean>
<!--SecurityManager即安全管理器,对全部的subject进行安全管理,它是shiro的核心,负责对所有的subject进行安全管理。-->
<bean name="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realms">
<list>
<ref bean="userRealm"/>
</list>
</property>
</bean>
<!--自定义realm-->
<bean name="userRealm" class="com.xcj.jquery_ajax.realm.UserRealm">
<property name="credentialsMatcher" ref="hashedCredentialsMatcher"/>
</bean>
<!--凭证匹配器,用于加密密码和验证密码服务-->
<bean name="hashedCredentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="MD5"/>
<property name="hashIterations" value="1"/>
</bean>
<!--开启shiro的注解-->
<bean id="advisorAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
<property name="proxyTargetClass" value="true"></property>
</bean>
</beans>
applicationContext.properties
#############################
## 数据库源 ##
#spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/jquery_ajax?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone = GMT
spring.datasource.username=root
spring.datasource.password=123456
# 下面为连接池的补充设置,应用到上面所有数据源中
# 初始化大小,最小,最大(连接数 = ((核心数 * 2) + 有效磁盘数))
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=10
# 配置获取连接等待超时的时间
spring.datasource.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
#用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
spring.datasource.validationQuery=SELECT 1 FROM DUAL
#建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
spring.datasource.testWhileIdle=true
#申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
spring.datasource.testOnBorrow=false
#归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
spring.datasource.testOnReturn=false
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.datasource.filters=stat,wall,slf4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合并多个DruidDataSource的监控数据
spring.datasource.useGlobalDataSourceStat=true
web.xml
applicationContext.xml中shiro的拦截器bean的name属性必须与web.xml的filter的name属性相同(配置了targetBeanName属性,则以targetBeanName为主)。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>jquery_ajax</display-name>
<!-- 配置前端控制器DispatcherServlet -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--springmvc.xml 是自己创建的SpringMVC全局配置文件,用contextConfigLocation作为参数名来加载
如果不配置 contextConfigLocation,那么默认加载的是/WEB-INF/servlet名称-servlet.xml,在这里也就是 springmvc-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,还可以写*.action等等,表示以.do结尾的或者以.action结尾的URL都由前端控制器DispatcherServlet来解析
第二种配置:/,所有访问的 URL 都由DispatcherServlet来解析,但是这里最好配置静态文件不由DispatcherServlet来解析
错误配置:/*,注意这里是不能这样配置的,应为如果这样写,最后转发到 jsp 页面的时候,仍然会由DispatcherServlet进行解析,
而这时候会找不到对应的Handler,从而报错!!! -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--设置字符编码过滤器-->
<filter>
<filter-name>characterEncodingFilter</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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
!--配置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>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- druid 监控过滤器 -->
<filter>
<filter-name>druidWebStatFilter</filter-name>
<filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
<init-param>
<param-name>exclusions</param-name>
<param-value>/assets/*,*.css,*.js,*.gif,*.jpg,*.png,*.ico,*.eot,*.svg,*.ttf,*.woff,*.jsp,*.tpl,/druid/*
</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>druidWebStatFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>druidStatView</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
<init-param>
<!-- 用户名 -->
<param-name>loginUsername</param-name>
<param-value>admin</param-value>
</init-param>
<init-param>
<!-- 密码 -->
<param-name>loginPassword</param-name>
<param-value>123456</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>druidStatView</servlet-name>
<url-pattern>/druid/*</url-pattern>
</servlet-mapping>
</web-app>
因为我们在applicationContext.xml中设置的统一异常页面处理:
<bean class="com.xcj.jquery_ajax.execption.MyExceptionResolver"/>
所以,我们来实现下MyExceptionResolver
package com.xcj.jquery_ajax.execption;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
public class MyExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
//e.printStackTrace();
log.error("异常原因:{} ; 异常信息:{}",e.getCause(),e.getMessage());
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", e.toString().replaceAll("\n", "<br/>"));
return mv;
}
}
error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>异常</title>
</head>
<h2>请联系管理员</h2><br/>
${exception}
</body>
</html>
以上,认证过程就搞定了。
(二)、演示效果
总结,此篇中的Shiro认证过程不算复杂,我在代码中做了详细的注释,应该也不难看懂。
如果你是刚学Shiro认证的话,不凡模仿下我这个例子。
若发现我有错误的话,欢迎指正,我们互相学习,拜拜!