基于:【狂神说Java】SpringBoot最新教程IDEA版通俗易懂
目录
1 Shiro架构
Shiro三个核心组件:Subject, SecurityManager 和 Realms.
- Subject:主体,可以看到主体可以是任何可以与应用交互的 “用户”;
- SecurityManager:相当于 SpringMVC 中的 DispatcherServlet 或者 Struts2 中的 FilterDispatcher;是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。
- Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。
2 使用SpringBoot整合Shiro
2.1 导入依赖
<!-- shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.6.0</version>
</dependency>
2.2 编写配置文件ShiroConfig
@Configuration
public class ShiroConfig {
//创建reaml对象,需要自定义
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
//SecurityManager
@Bean
public DefaultWebSecurityManager SecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm,因为是他的管理者
securityManager.setRealm(userRealm);
return securityManager;
}
//ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
// 设置SecurityManager
factoryBean.setSecurityManager(securityManager);
// 添加过滤器
LinkedHashMap<String, String> filterMap = new LinkedHashMap<>();
/*
anon:不需要登录就能访问,一般用于静态资源,或者移动端接口
authc:需要登录认证才能访问的资源
perms:验证用户是否拥有资源权限
roles:验证用户是否拥有资源角色
user:用户已经身份验证 / 记住我登录的都可
*/
// 直接访问
filterMap.put("/","anon");
filterMap.put("/index","anon");
filterMap.put("/user/login","anon");
//登录了的才能查看自己和注销
filterMap.put("/user/me","authc");
filterMap.put("/user/layout","authc");
// 登录了都能访问鸢尾花
filterMap.put("/iris","authc");
// filterMap.put("/pm25","authc");
// 授权canVisitPM25的用户才能访问pm链接
filterMap.put("/pm25","perms[canVisitPM25]");
// 把map放进去
factoryBean.setFilterChainDefinitionMap(filterMap);
// 设置登录失败的跳转权限
factoryBean.setLoginUrl("/pleaseLogin");
// 权限不足跳的链接
factoryBean.setUnauthorizedUrl("/insufficientAuthority");
return factoryBean;
}
}
这是Shiro的核心配置文件,依次有三个核心对象,UserRealm
、DefaultWebSecurityManager
、ShiroFilterFactoryBean
基本是固定写法,需要根据业务来配置的就是过滤器的规则,即哪些页面需要哪些权限才能访问。
2.2 编写自定义的Realm
public class UserRealm extends AuthorizingRealm {
@Autowired
UserServiceImpl userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行shiro授权");
// 权限
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission("canVisitPM25");
// 参数principalCollection就是登录成功后,user对象信息,可以根据数据库情况决定是否授权
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行shiro认证");
// 把传入参数的token转为UsernamePasswordToken
UsernamePasswordToken userToken=(UsernamePasswordToken)authenticationToken;
// 数据库取信息
User user = userService.getUserByUname(userToken.getUsername());
if(user==null) return null;//没有这个用户
// 判断密码
return new SimpleAuthenticationInfo(user,user.getUpassword(),"");
}
}
配置文件定义了完整的访问权限和规则,那么Relm
就是具体的授权和认证功能,二者都需要去数据库查询;
2.3 ShiroService服务类
@Service
public class ShiroServiceImpl implements ShiroService {
@Override
public String login(String username, String pwd) {
// 获得当前访问的用户
Subject subject = SecurityUtils.getSubject();
// 封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username, DigestUtils.md5DigestAsHex(pwd.getBytes()));
// 开始验证
try {
subject.login(token);
return "登录成功";
}catch (UnknownAccountException e){//用户名不存在
return "用户名不存在";
}catch (IncorrectCredentialsException e){//密码错误
return "密码错误";
}
}
@Override
public String showMe() {
return SecurityUtils.getSubject().toString();
}
@Override
public String logout() {
SecurityUtils.getSubject().logout();
return "注销成功";
}
}
SecurityUtils.getSubject()
可以获得当前的用户,然后对其进行相应的权限控制。
3 原理
- SpringBoot对于Shiro的封装较深,逻辑层面不是很好理解,直接使用官网的非web项目例子比较好理解;
- 如何判断当前访问的用户就是已经登录过的,还需要了解
ThreadLocal
相关的知识; - 如何绑定浏览器访问者和
subject
用户,需要靠cookie
来保证,这部分知识可以通过实践的出来;