1、SSM整合shiro
1.1 添加依赖
和之前的SSM相比,整合shiro的时候需要添加一个依赖
org.apache.shiro
shiro-spring
1.4.0
shiro-spring这个依赖包含了shiro-core与shiro-web,添加之后就不再需要添加shiro-core和shiro-web了。
1.2 spring-shiro.xml配置
spring-shiro.xml
/index.jsp=anon
/main.jsp=authc
/manager.jsp=roles[manager]
/guest.jsp=roles[guest]
这个xml文件配置了四个bean:ShiroFilterFactoryBean、DefaultWebSecurityManager、MyRealm、LifecycleBeanPostProcessor。ShiroFilterFactoryBean的作用是配置过滤器工厂,id要和稍后要说的web.xml中的shiro过滤器的名称一样,指定securityManager,等于页面,过滤页面。DefaultWebSecurityManager这个的主要作用就是配置realm,如果有多个realm就要配置realms,MyRealm是我们自定义的Realm,然后LifecycleBeanPostProcessor是一个AOP的实现。
1.3 web.xml
shiroFilter
org.springframework.web.filter.DelegatingFilterProxy
targetFilterLifecycle
true
contextConfigLocation
classpath:*.xml
org.springframework.web.context.ContextLoaderListener
shiroFilter
/*
dispatcherServlet
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring-mvc.xml
dispatcherServlet
/
encode
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
encode
/*
和shiro相关的就是一个过滤器和一个监听器,这个过滤器类是DelegatingFilterProxy,name应该和spring-shiro.xml中ShiroFilterFactoryBean的id一样,监听器是ContextLoaderListener,文本加载监听器,context-param这个标签将classpath目录下的所有xml文件读取进来。
1.4 MyRealm
MyReanl.java
package com.qianfeng.shiro;
import com.qianfeng.entity.Employee;
import com.qianfeng.entity.Permission;
import com.qianfeng.entity.Roles;
import com.qianfeng.service.IEmployeeService;
import org.apache.shiro.authc.*;
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 javax.annotation.Resource;
import java.util.List;
/**
* @author huwen
*/
public class MyRealm extends AuthorizingRealm {
@Resource
private IEmployeeService employeeService;
/**
* 授权方法
* @param principalCollection principal的集合,可以理解为各种用户身份的集合,比如用户名、邮箱、手机号等
* @return 返回的是授权信息,包括角色与权限
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//SimpleAuthorizationInfo是AuthorizationInfo的子类
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//得到可用的principal,然后转换为字符串
String empName = getAvailablePrincipal(principalCollection).toString();
//调用service
List roles = employeeService.getAllRolesByEmpName(empName);
for (Roles role : roles) {
info.addRole(role.getRoleName());
}
List permissions = employeeService.getAllPermissionsByEmpName(empName);
for (Permission permission : permissions) {
info.addStringPermission(permission.getPermName());
}
return info;
}
/**
* 这个方法用于认证
* @param authenticationToken 用户名与密码
* @return 认证信息
* @throws AuthenticationException 可能引发的异常
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
AuthenticationInfo info = null;
//将authenticationToken强转为usernamePasswordToken,向下转型能够成功因为我们知道用户输入的是用户名与密码
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//从token中获取用户名与密码传给service,最后交给dao从数据库中查询
String username = token.getUsername();
//使用字节数组存储密码更为安全,因为字节数组是可变的,字符串是不可变的存在常量池中
char[] password = token.getPassword();
String pass = new String(password);
Employee emp = employeeService.getEmployeeByNameAndPassword(username,pass);
//如果查询到的数据不为空,就构造一个SimpleAuthenticationInfo对象,将用户名与密码放在里里面
if(emp!=null && emp.getEmpId()!=0){
//getName()获取到的是当前Realm的标识,因为可以自定义多个Realm,不同的Realm需要区分
info = new SimpleAuthenticationInfo(username,pass,getName());
}
return info;
}
}
MyRealm继承自AuthorizingRealm,重写两个方法,第一个方法用于授权,第二个方法用于认证。
1.5 EmployeeController
package com.qianfeng.controller;
import com.qianfeng.entity.Employee;
import com.qianfeng.service.IEmployeeService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import javax.annotation.Resource;
@Controller
public class EmployeeController {
@Resource
private IEmployeeService employeeService;
@PostMapping("/login")
public String empLogin(Employee employee){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(employee.getEmpName(),employee.getPassword());
try {
subject.login(token);
return "main";
} catch (AuthenticationException e) {
e.printStackTrace();
}
return "index";
}
}
可以看到和之前的web项目的不同,不需要我们手动创建配置工厂生成实例了。
2、SpringBoot整合shiro
2.1 添加依赖
这里shiro需要的依赖和SSM项目中的依赖一样,都是shiro-spring。如果想要在thymeleaf中使用shiro标签,需要添加下面的依赖:
com.github.theborakompanioni
thymeleaf-extras-shiro
2.0.0
然后载html标签中添加xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"。
2.2 MyRealm
package com.qianfeng.shiro;
import com.qianfeng.entity.Employee;
import com.qianfeng.entity.Permission;
import com.qianfeng.entity.Roles;
import com.qianfeng.service.IEmployeeService;
import org.apache.shiro.authc.*;
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.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
/**
* @author huwen
*/
@Component("myRealm")
public class MyRealm extends AuthorizingRealm {
@Resource
private IEmployeeService employeeService;
/**
* 授权方法
* @param principalCollection principal的集合,可以理解为各种用户身份的集合,比如用户名、邮箱、手机号等
* @return 返回的是授权信息,包括角色与权限
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//SimpleAuthorizationInfo是AuthorizationInfo的子类
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//得到可用的principal,然后转换为字符串
String empName = getAvailablePrincipal(principalCollection).toString();
//调用service
List roles = employeeService.getAllRolesByEmpName(empName);
for (Roles role : roles) {
info.addRole(role.getRoleName());
}
List permissions = employeeService.getAllPermissionsByEmpName(empName);
for (Permission permission : permissions) {
info.addStringPermission(permission.getPermName());
}
return info;
}
/**
* 这个方法用于认证
* @param authenticationToken 用户名与密码
* @return 认证信息
* @throws AuthenticationException 可能引发的异常
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
AuthenticationInfo info = null;
//将authenticationToken强转为usernamePasswordToken,向下转型能够成功因为我们知道用户输入的是用户名与密码
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//从token中获取用户名与密码传给service,最后交给dao从数据库中查询
String username = token.getUsername();
//使用字节数组存储密码更为安全,因为字节数组是可变的,字符串是不可变的存在常量池中
char[] password = token.getPassword();
String pass = new String(password);
Employee emp = employeeService.getEmployeeByNameAndPassword(username,pass);
//如果查询到的数据不为空,就构造一个SimpleAuthenticationInfo对象,将用户名与密码放在里里面
if(emp!=null && emp.getEmpId()!=0){
//getName()获取到的是当前Realm的标识,因为可以自定义多个Realm,不同的Realm需要区分
info = new SimpleAuthenticationInfo(username,pass,getName());
}
return info;
}
}
MyRealm这个类和SSM项目基本一样,唯一的不同是加上了@Component("myRealm")这个注解
2.3 ShiroConfig
ShiroConfig.java
package com.qianfeng.config;
import com.qianfeng.shiro.MyRealm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
/**
* 该方法用于返回过滤器工厂
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager")DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map map = new HashMap<>(10);
map.put("/index.html","anon");
map.put("/main.html","authc");
shiroFilterFactoryBean.setLoginUrl("/login.html");
shiroFilterFactoryBean.setUnauthorizedUrl("/unauth.html");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
/**
* 设置realm为自定义的Realm,返回securityManager
* @param realm
* @return
*/
@Bean(name = "defaultWebSecurityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("myRealm")MyRealm realm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
return securityManager;
}
/**
* 开启shiro的注解,需要借助SpringAOP扫描Shiro注解的类,来进行安全校验
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
/**
* 开启aop的注解支持
* @param defaultWebSecurityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager defaultWebSecurityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager);
return authorizationAttributeSourceAdvisor;
}
}
由于springboot不使用xml文件进行配置,所以采用Java类的方式对shiro进行设置,整个类上有一个注解@Configuration,共有四个方法每个方法都有一个@Bean注解,方法名可以自定义,但是参数与返回值必须对应,第一个方法的作用是设置并返回过滤器工厂,第二个方法的作用是设置securityManager并返回,下面那两个方法都是和AOP相关的。