搭建好SSM项目
项目中引入相关依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!-- 添加mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.1</version>
</dependency>
<!-- 添加junit用于实现单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- 添加mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!-- 整合mybatis时需要添加如下两个jar文件-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!--整合log4j2,目的是便于我们根据日志的输出调试代码
一般添加完依赖会在类路径添加一个log4j2.xml文件
-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.7</version>
</dependency>
<!-- 整合jackson (在本项目中对请求的响应我们要采用json格式) -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9.3</version>
</dependency>
<!--添加JSTL标签库的支持 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- 整合文件上传功能 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- 整合shiro 安全框架 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.10</version>
</dependency>
web.xml文件中注册shiro过滤器
<?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_2_5.xsd" version="2.5">
<display-name>tzms</display-name>
<filter>
<filter-name>CharacterEncoding</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>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-*.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- shiro配置过滤器 DelegatingFilterProxy通过代理模式将spring容器中的bean和filter关联起来 -->
<filter>
<filter-name>delegatingFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<!-- 设置spring容器filter的bean id,如果不设置则找与filter-name一致的bean -->
<init-param>
<param-name>targetBeanName</param-name>
<param-value>shiro</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>delegatingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
添加shiro的配置文件
注意shiro的配置可以添加在spring的配置文件中。但是为了便于管理我们再单独创建一个shiro的配置文件,里面的Schema还是spring的。
同时web.xml文件中加载spring的位置也需要调整
spring-shiro.xml中的内容
<?xml version="1.0" encoding="UTF-8"?>
<beans default-lazy-init="true"
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/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 配置web.xml中过滤器对应的spring容器的bean -->
<bean id="shiro" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean" >
<!-- 注册SecurityManager -->
<property name="securityManager" ref="securityManager"></property>
<!-- 登录时访问的链接 -->
<property name="loginUrl" value="/loginUI"></property>
<!-- 用户无权限时需要访问的页面 -->
<property name="unauthorizedUrl" value="/errorUI"></property>
<!-- 设置 过滤器链 -->
<property name="filterChainDefinitions">
<value>
/bootstrap/** = anon
/dist/** = anon
/images/** = anon
/jquery/** = anon
/tzms/** = anon
<!-- 所有请求匿名访问 /** = anon -->
<!-- 点击登录 不需要认证 -->
/login = anon
<!-- 退出 此方法会清空shiro中session的数据 -->
/logout = logout
<!-- 所有的url都必须要认证通过后才可以访问 -->
/** = authc
</value>
</property>
</bean>
<!-- 配置安全管理器对象 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="shiroUserRealm"></property>
</bean>
<!-- 配置自定义Realm -->
<bean id="shiroUserRealm" class="com.tanzhou.tzms.common.service.impl.ShiroRealmsOne">
<!-- 配置凭证匹配器 -->
<property name="credentialsMatcher" ref="credentialsMatcher"></property>
</bean>
<!-- 定义凭证匹配器 -->
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- 匹配散列算法 -->
<property name="hashAlgorithmName" value="MD5"></property>
<!-- 匹配散列次数 -->
<property name="hashIterations" value="1"></property>
</bean>
</beans>
常见shiro过滤器
过滤器名称 | 过滤器类 | 说明 |
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 | 如果访问一个已知用户,比如记住我功能,走这个过滤器 |
创建Realm接口和实现类
完成realm的认证功能
SysShiroServiceImpl类内容
public class SysShiroServiceImpl implements SysShiroService {
@Override
public void login(String username, String password) {
Subject subject = SecurityUtils.getSubject();
if(subject.isAuthenticated())return;
// 把用户名和密码封装为 UsernamePasswordToken 对象
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try{//登录认证 - 调用userRealm
subject.login(token);
}catch (IncorrectCredentialsException ice) {
throw new ServiceException("密码错误!");
} catch(AuthenticationException ae){
ae.printStackTrace();
throw new ServiceException("认证失败");
}
}
}
自定义Realm认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username =(String)token.getPrincipal();
//通过用户名从数据库中查询到数据返回对象
SysUser userInfo = userDao.findObjectByName(username);
//从对象中得到加密后的密码
String password = userInfo.getPassword();
//从对象中获取到盐值
String salt = userInfo.getSalt();
//将盐值专为十六进制
ByteSource saltSource = ByteSource.Util.bytes(salt);
//通过用户ID从数据库中查询到首页左侧导航栏要显示的菜单
List<Map<String, Object>> map = sysService.findUserMenus(userInfo.getId());
//创建CurrentUser类 将查询得到的用户id、用户名称和用户需要显示的菜单放到创建好的对象中
CurrentUser user = new CurrentUser();
user.setUserid(userInfo.getId());
user.setUsername(userInfo.getUsername());
user.setMenus(map);
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,password,saltSource,this.getName());
return info;
}
完成controller
@RequestMapping("/login")
@ResponseBody
public JsonResult login(HttpSession session,String username,String password){
System.out.println(username+"/"+password);
loginService.login(username, password);
Subject subject = SecurityUtils.getSubject();
System.out.println("是否认证成功:"+subject.isAuthenticated());
return new JsonResult();
}
结果为
密码错误
密码正确
执行原理流程