shrio的作用已经不用说了,就直接说用吧,当然我们的系统要求没有很高,所以我也学习的比较简单。
第一步:jar
<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>
第二步:配置web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<!--加载springmvc和spring配置文件的位置,添加sprin- shiro.xml--!>
<param-value>classpath:spring/applicationContext-*.xml,classpath:spring/spring-shiro.xml</param-value>
</context-param>
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
第三步:
spring-shrio.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
default-lazy-init="true">
<description>Shiro Configuration</description>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--设置自定义realm -->
<property name="realms" ref="monitorRealm" />
<!-- 缓存管理器 -->
<property name="cacheManager" ref="cacheManager"/>
</bean>
<!-- 会话管理器 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!-- 设置超时时间 -->
<property name="globalSessionTimeout" value="18000"/>
<property name="deleteInvalidSessions" value="true"/>
<property name="sessionValidationSchedulerEnabled" value="true"/>
<property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
<property name="sessionDAO" ref="sessionDAO"/>
<property name="sessionIdCookieEnabled" value="true"/>
<property name="sessionIdCookie" ref="sessionIdCookie"/>
</bean>
<!--自定义Realm 继承自AuthorizingRealm -->
<bean id="monitorRealm" class="com.yzb.loanplatform.manager.util.shrio.ShrioRealm">
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="MD5"/>
<!--true means hex encoded, false means base64 encoded -->
<property name="storedCredentialsHexEncoded" value="true"/>
<!-- 迭代次数 -->
<property name="hashIterations" value="1024" />
</bean>
</property>
</bean>
<!-- Shiro主过滤器本身功能十分强大,其强大之处就在于它支持任何基于URL路径表达式的、自定义的过滤器的执行 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<!-- 要求登录时的链接,非必须的属性,默认会自动寻找Web工程根目录下的"/login.jsp"页面 -->
<property name="loginUrl" value="/login" />
<!-- 用户访问未对其授权的资源时,所显示的连接 -->
<!-- <property name="unauthorizedUrl" value="/error/noperms.jsp" /> -->
<property name="filterChainDefinitions">
<value>
<!--
Anon:不指定过滤器
Authc:验证,这些页面必须验证后才能访问,也就是我们说的登录后才能访问。
-->
/login.ftl = anon
/system/**= authc
/orderInfo/findOrderInfo= authc
/orderInfo/ucfRefundCallback= anon
/static/**=anon
</value>
</property>
</bean>
<!--
用户授权信息Cache -->
<bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<!-- AOP式方法级权限检查 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true" />
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
</beans>
第四步:springmvc中配置:其作用是在conroller中使用注解方法实现权限拦截
<!-- 为使用AOP注解方式控制权限 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true" />
</bean>
以上配置就是所有配置,现在仔细说下spring-shrio.xml中的几个重点:
1,自定义的realm:shrio中核心,有SecurityManager管理,
<bean id="securityManager"class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--设置自定义realm -->
<property name="realms" ref="monitorRealm" />
</bean>
<!--自定义Realm 继承自AuthorizingRealm -->
<bean id="monitorRealm" class="**com.yzb.loanplatform.manager.util.shrio.ShrioRealm**">
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- 密码加密方式 -->
<property name="hashAlgorithmName" value="MD5"/>
<property name="storedCredentialsHexEncoded" value="true"/>
<!-- 次数加密次数 -->
<property name="hashIterations" value="1024" />
</bean>
</property>
</bean>
重点来了,ShrioRealm.java用来实现登陆身份验证和首选操作;
package com.yzb.loanplatform.manager.util.shrio;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
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.springframework.beans.factory.annotation.Autowired;
import com.yzb.loanplatform.pojo.Employee;
import com.yzb.loanplatform.pojo.Permission;
import com.yzb.loanplatform.pojo.Role;
import com.yzb.loanplatform.service.EmployeeService;
import com.yzb.loanplatform.vo.EmployeeVo;
/**
* @ClassName: ShrioRealm
* @Description: TODO(这里用一句话描述这个类的作用)
* @author zhaofq
* @date 2017年8月10日
*/
public class ShrioRealm extends AuthorizingRealm {
private static Logger loger = Logger.getLogger(ShrioRealm.class.getName());
@Autowired
private EmployeeService employeeService;
/**
* 授权操作
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 获取当前登录的用户名,等价于(String)principals.fromRealm(this.getName()).iterator().next()
String account = (String) super.getAvailablePrincipal(principals);
EmployeeVo employeeVo = new EmployeeVo();
employeeVo.setPhone(account);
List<String> roles = new ArrayList<String>();
List<String> permissions = new ArrayList<String>();
// //从数据库中获取当前登录用户的详细信息
Employee employee = employeeService.getEmployeeRoleAanPermissions(employeeVo);
if (employee != null) {
// 实体类User中包含有用户角色的实体类信息
if (employee.getRoleList() != null && employee.getRoleList().size() > 0) {
// 获取当前登录用户的角色
for (Role role : employee.getRoleList()) {
roles.add(role.getRoleCode());
// 实体类Role中包含有角色权限的实体类信息
if (role.getPermissionList() != null && role.getPermissionList().size() > 0) {
// 获取权限
for (Permission pmss : role.getPermissionList()) {
if (!StringUtils.isEmpty(pmss.getUrl())) {
permissions.add(pmss.getUrl());
}
}
}
}
}
} else {
throw new AuthorizationException();
}
// 为当前用户设置角色和权限
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRoles(roles);
info.addStringPermissions(permissions);
return info;
}
/**
* 身份验证操作
*/
@SuppressWarnings("unused")
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
String userName = String.valueOf(token.getPrincipal()); // 得到身份(用户名)
String passWord = new String((char[]) token.getCredentials()); // 得到认证/凭证(密码)
// 从数据库中查询用户用信息
EmployeeVo employeeVo = new EmployeeVo();
employeeVo.setPhone(userName);
Employee employee = employeeService.getEmployee(employeeVo);
Employee employee2 = employeeService.getEmployeeRoleAanPermissions(employeeVo);
if (employee != null) {
if ("1".endsWith(employee.getStatus().toString())) { // 判断帐号是否锁定
throw new LockedAccountException();// 抛出 帐号锁定异常
}
// 此处无需比对,比对的逻辑Shiro会做,我们只需返回一个和令牌相关的正确的验证信息
byte[] myByte = employee.getSalt().getBytes();
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(employee.getPhone(),
employee.getEmpPassword(), ByteSource.Util.bytes(myByte), this.getName());
// simpleAuthenticationInfo.setCredentialsSalt(salt);
return simpleAuthenticationInfo;
} else {
// 没有返回登录用户名对应的SimpleAuthenticationInfo对象时,就会在LoginController中抛出UnknownAccountException异常
return null;
}
}
@Override
public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
super.clearCachedAuthorizationInfo(principals);
}
@Override
public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
super.clearCachedAuthenticationInfo(principals);
}
@Override
public void clearCache(PrincipalCollection principals) {
super.clearCache(principals);
}
public void clearAllCachedAuthorizationInfo() {
getAuthorizationCache().clear();
}
public void clearAllCachedAuthenticationInfo() {
getAuthenticationCache().clear();
}
public void clearAllCache() {
clearAllCachedAuthenticationInfo();
clearAllCachedAuthorizationInfo();
}
}
有两个重要的方法:
1,doGetAuthenticationInfo:用来做身份认证,当用户输入用户名密码后在登陆的controller中通过:
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
Subject currentUser = SecurityUtils.getSubject();
token.setRememberMe(true);
currentUser.login(token);
实现对doGetAuthenticationInfo的调用,具体作用就是把页面发送过来的用户名和密码传入到doGetAuthenticationInfo方法中,
使用用户名去重是否有对应的用户,如果找到就调用shrio内部的密码校验方法,
// 此处无需比对,比对的逻辑Shiro会做,我们只需返回一个和令牌相关的正确的验证信息
byte[] myByte = employee.getSalt().getBytes();
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(employee.getPhone(),
employee.getEmpPassword(), ByteSource.Util.bytes(myByte), this.getName());
// simpleAuthenticationInfo.setCredentialsSalt(salt);
return simpleAuthenticationInfo;
这里你只需要把你你查询数据获取到的用户名,密码,言放到SimpleAuthenticationInfo就可以了,可以看看看看源码是怎么实现的校验的,不再多说,
根据返回校验结果就会提示是否校验通过。
2,授权方法:doGetAuthorizationInfo:就是查看操作用户拥有的资源权限,
主要是根据用户名获取对应角色,权限的资源。
次方法调用时在controller总通过注解的方式:
@RequiresRoles(logical=Logical.OR,value={“admin”,”adminEmp”})
页面时通过shrio标签。
<shiro:hasAnyRoles name="admin,adminEmp" ><li><a href="${request.contextPath}/employee/employeePage">员工管理</a></li></shiro:hasAnyRoles>
当然我这里是把菜单写死了,所以不能控制菜单是否显示,而是在操作时提示没有权限,
原理大概是党操作菜单之后就会通过@RequiresRoles查看当强用户是否用户此角色,角色是否用户权限。
当也有直接控制按钮的方式我就不在说了,
public class BaseController {
/**
* 登录认证异常
*/
@ExceptionHandler({ UnauthenticatedException.class, AuthenticationException.class })
public String authenticationException(HttpServletRequest request, HttpServletResponse response) {
if (WebUtilsPro.isAjaxRequest(request)) {
// 输出JSON
Map<String,Object> map = new HashMap<>();
map.put("code", "-999");
map.put("message", "未登录");
return null;
} else {
return "redirect:/system/login";
}
}
/**
* 权限异常
*/
@ExceptionHandler({ UnauthorizedException.class, AuthorizationException.class,ServletException.class })
public String authorizationException(HttpServletRequest request, HttpServletResponse response) {
DataResponse data = new DataResponse();
// 输出JSON
Map<String,Object> map = new HashMap<>();
map.put("code", "-999");
map.put("msg", "无权限");
data.setCode(200);
data.setData(map);
return "/notPermission";
}
}
登陆异常和无权限时提示,让每个使用权限的controller继承此controler就可以了,
最后角色权限表:
DROP TABLE IF EXISTS `MTB_ROLE`;
CREATE TABLE `MTB_ROLE` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT NULL,
`description` varchar(50) DEFAULT NULL,
`roleCode` varchar(255) DEFAULT NULL,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4;
DROP TABLE IF EXISTS `MTB_PERMISSION`;
CREATE TABLE `MTB_PERMISSION` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
`description` varchar(200) DEFAULT NULL,
`url` varchar(255) DEFAULT NULL,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4;
DROP TABLE IF EXISTS `MTB_ROLE_PERMISSION`;
CREATE TABLE `MTB_ROLE_PERMISSION` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`role_Id` int(36) DEFAULT NULL,
`permission_Id` int(36) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8mb4;
DROP TABLE IF EXISTS `MTB_EMPLOYEE_ROLE`;
CREATE TABLE `MTB_EMPLOYEE_ROLE` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`employee_Id` int(36) DEFAULT NULL,
`role_Id` int(36) DEFAULT NULL,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8mb4;
还有一个就是员工表了,就
补充1: 登陆之后显示用户名字,在页面用<@shiro.principal/>就可以获得当强登陆用户名,当然也可以用${username}单这个是用了controller返回的对象的mv.addObject(“username”, username);推荐用<@shiro.principal/>