1、环境搭建
1、在web.xml中配置过滤器
shiroFilter
org.springframework.web.filter.DelegatingFilterProxy
targetFilterLifecycle
true
shiroFilter
/*
注意,过滤器的名字shiroFilter可以自定义,但是spring配置文件中的bean的id默认要与该filter-name相同,如果想要改变默认设置,可以配置targetBeanName属性
2、在spring配置文件中集成shrio
shiro框架的一切功能都是围绕securityManager展开,所以集成shiro最核心的bean就是org.apache.shiro.web.mgt.DefaultWebSecurityManager,在这个bean中可以实现缓存、认证授权和记住我等多种功能。除了securityManager,还有一个配置也很重要,那就是之前配置在web.xml中的过滤器的工厂org.apache.shiro.spring.web.ShiroFilterFactoryBean。这个bean的id默认与filter-name相同,它的内部持有了securityManage实例,内部可以配置认证成功后指向的资源、认证资源和未授权指向的资源以及角色权限配置。
2、shiro配置详解
注:shiro中的资源指服务器内一切内容,包括java代码、接口、js、css、图片和一切其他资源
1、过滤器工厂类
这个类用来生成过滤器实例,并且提供了众多属性供过滤器使用:
还可以配置shiro的授权,有两种配置方法
1、在配置文件中写入
/user/login = anon
/user/registerView = anon
/user/register = anon
/user/logout = logout
/user/addView = roles[emp]
/user/modifyView = roles[manager]
/user/removeView = roles[admin]
/user/** = user
/static/** = anon
2、自己创建工厂生成filterChainDefinitionMap
shiroFilter中有一个filterChainDefinitionMap对象属性,它是一个实现了Map接口的对象,我们可以利用自建工厂添加拦截配置
这里是shiroFilter的filterChainDefinitionMap属性配置
工厂类由自己实现 类名称和方法自行定义,这里使用了如下实现
package com.fan.shiro.util;
import com.fan.shiro.pojo.FilterChainDefinition;
import com.fan.shiro.service.IFilterChainDefinitionService;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.LinkedHashMap;
import java.util.List;
public class FilterChainDefinitionMapBuilder {
@Autowired
private IFilterChainDefinitionService filterChainDefinitionService;
public LinkedHashMap buildFilterChainDefinitionMap() {
//调用业务层从数据库中查出拦截资源的列表
List filterChainDefinitions = filterChainDefinitionService.findAllFilterChainDefinition();
LinkedHashMap map = new LinkedHashMap<>();
for (FilterChainDefinition f : filterChainDefinitions) {
map.put(f.getApi(), f.getFilter());
}
return map;
}
}
3、注意
1、不论在配置文件中直接配置,还是利用工厂返回map,拦截资源的配置都是按照顺序先后执行,例如:
/user/register = anon
/user/register = authc
/user/** = anon
/user/modifyView = authc
/user/removeView = user
所以,即使在数据库中添加配置数据的时候也一定要注意先后顺序。
2、权限和认证(记住我)可以进行组合
比如:authc,roles[pay]表示这个角色必须在认证后才能生效,在记住我状态时无法访问,必须先登录认证
2、securityManager
securityManager是shiro的核心,重要功能都需要它实现
1、缓存
这里使用了ecach缓存
2、realm实现认证和授权
realm想要同时具有认证和授权的功能,需要继承AuthorizingRealm类
以下是reaml的实现
package com.fan.shiro.realms;
import com.fan.shiro.pojo.Permission;
import com.fan.shiro.pojo.Role;
import com.fan.shiro.pojo.User;
import com.fan.shiro.service.IPermissionService;
import com.fan.shiro.service.IRoleService;
import com.fan.shiro.service.IUserService;
import org.apache.shiro.SecurityUtils;
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.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ShiroRealm extends AuthorizingRealm {
@Autowired
IUserService userService;
@Autowired
IRoleService roleService;
@Autowired
IPermissionService permissionService;
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//从authenticationToken取出UsernamePasswordToken
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String userName = token.getUsername();
User user = userService.findUserByUserName(userName);
if (user == null) {
throw new UnknownAccountException("用户不存在!");
}
if (user.getStatus().equals("0")) {
throw new LockedAccountException("该用户已被锁定!");
}
//获取盐值
ByteSource byteSource = ByteSource.Util.bytes(userName);
//通过盐值加密验证用户 密码不匹配会抛出IncorrectCredentialsException异常
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), byteSource, this.getName());
//通过验证后将用户对象存储到session中
Session session = SecurityUtils.getSubject().getSession();
session.setAttribute("userInfo", user);
return simpleAuthenticationInfo;
}
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
User user = (User) principalCollection.getPrimaryPrincipal();
Set roles = new HashSet<>();
//查出用户的角色 并将所有角色添加到set集合中
List roleList = roleService.findRoleByUserId(user.getUserId());
for (Role r : roleList) {
roles.add(r.getRoleName());
}
//将set注入到SimpleAuthorizationInfo实例中
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roles);
//查出用户权限
List permissionList = permissionService.findPermissionByUserId(user.getUserId());
List permissions = new ArrayList<>();
for (Permission p : permissionList) {
permissions.add(p.getPermissionName());
}
//调用addStringPermissions将权限列表加入simpleAuthorizationInfo
simpleAuthorizationInfo.addStringPermissions(permissions);
//返回实例 告知shiro框架该用户具有的角色和权限
return simpleAuthorizationInfo;
}
}
3、rememberMe记住我
记住我功能与认证功能不一样,记住我功能开启后,shiro会检测客户端是否有rememberme cookie,如果有就会对该cookie进行验证,如果没有就会生成一个cookie并返回给客户端。在拦截配置中user关键字就是针对记住我。==记住我在controller中必须在Subject的login方法之前调用==
3、其它配置
4、controller层如何实现认证
@RequestMapping("/login")
@ResponseBody
public ResponseResult login(User user, String remember) {
ResponseResult responseResult = new ResponseResult("200", "登录成功!");
//1、获取当前Subject用户
Subject currentUser = SecurityUtils.getSubject();
//2、判断当前用户是否已经认证过
if (currentUser.isAuthenticated()) {
responseResult.setCode("200");
responseResult.setMessage("用户已登录!");
return responseResult;
}
//3、获得UsernamePasswordToken
UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), user.getPassword());
//4、进行用户身份验证
try {
//4.1判断用户是否设置了记住我选项,该方法必须在认证之前调用
boolean flag = false;
if (remember != null) {
flag = "remember".equals(remember);
}
token.setRememberMe(flag);
//4.2 调用Subject的login方法进行验证 如果验证不通过会抛出异常在catch中处理 验证通过则将用户加入
//session中
currentUser.login(token);
} catch (UnknownAccountException uae) {
responseResult.setCode("404");
responseResult.setMessage("用户不存在!");
return responseResult;
} catch (IncorrectCredentialsException ice) {
responseResult.setCode("400");
responseResult.setMessage("密码错误!");
return responseResult;
} catch (LockedAccountException lae) {
responseResult.setCode("300");
responseResult.setMessage("该用户已被锁定!");
return responseResult;
} catch (AuthenticationException ae) {
responseResult.setCode("500");
responseResult.setMessage("登录失败,请重试!");
return responseResult;
}
return responseResult;
}
3、总结
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序,JavaSE环境和JavaEE环境都可以使用 (抄来的)