SpringBoot与Shiro整合
Shiro基本概念
- 先从官网盗一点概念下来:
- Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.
- Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
Apache Shiro 体系结构
Apache Shiro 体系结构- 1、Authentication 认证 ---- 用户登录
- 2、Authorization 授权 --- 用户具有哪些权限
- 3、Cryptography 安全数据加密
- 4、Session Management 会话管理
- 5、Web Integration web系统集成
- 6、Interations 集成其它应用,spring、缓存框架
关于Shiro的Quickstart
官方Quickstart:
http://shiro.apache.org/10-minute-tutorial.html
Springboot 整合Shiro
- 本次实验基于Springboot的一个基本登录案例来测试Shiro的安全认证、授权功能
- 要使用Shiro我们需要ShiroFilterFactoryBean
- 而ShiroFilterFactoryBean需要设置DefaultWebSecurityManager管理器
- 而DefaultWebSecurityManager需要设置UserRealm
- 于是整个shiro的配置就变成这样:
package zstu.edu.springboot.shiro;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* shiro的配置类
* Created by CHEN on 2019/7/20.
*/
@Configuration
public class ShiroConfig {
/**
* 创建ShiroFilterFactoryBean
*
* @return
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/**
* Shiro内置过滤器,可以实现权限相关的拦截器
* 常用过滤器:
* anon:无需认证(登录)可以访问
* authc:必须认证才可以访问
* user:如果使用rememberMe的功能可以直接访问
* perms:该资源必须得到资源权限才可以访问
* role:该资源必须得到角色权限才可以访问
*/
Map<String,String> filterMap = new LinkedHashMap<>();
// filterMap.put("/add","authc");
// filterMap.put("/update","authc");
//可以这么写
//这里应当注意顺序,上下换一下就算设置了anon还是会被拦截,
//而且目前的顺序会先调用/testThymeleaf,然后再调用login,login还是调用了,只是因为权限是anon所以才没有彖传
//testThymeleaf..........
//login................
filterMap.put("/testThymeleaf","anon");
filterMap.put("/login","anon");
//授权过滤器
//当前授权拦截后shiro会自动跳转到未授权页面
filterMap.put("/add","perms[user:add]");
filterMap.put("/*","authc");
//修改默认登陆界面
shiroFilterFactoryBean.setLoginUrl("/tologin");
//设置未授权提示页面
shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
/**
* @Qualifier拿到容器中的UserRealm对象
* 创建DefaultWebSecurityManager
*/
@Bean(name="securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联一个Realm
securityManager.setRealm(userRealm);
return securityManager;
}
/**
* 创建Realm
*
* @return
*/
@Bean(name = "userRealm")
public UserRealm getRealm() {
return new UserRealm();
}
}
- 这里需要注意的是我们需要把定义的组件加到容器中,这样我们才能找到这些组件,@Bean中的name属性也可以不用设置,直接用他的默认名即可
- 而UserRealm也是一个自定义类,他需要继承AuthorizingRealm类
package zstu.edu.springboot.shiro;
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;
/**
* 自定义Realm
* Created by CHEN on 2019/7/20.
*/
public class UserRealm extends AuthorizingRealm {
/**
* 执行授权逻辑
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权逻辑");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//应当对接数据库,这里为了方便,直接写死
info.addStringPermission("user:add");
return info;
}
/**
* 执行认证的逻辑
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证逻辑");
//后续应当从数据库中获得,先写死
String name = "123";
String password = "123";
//判断用户名
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
if (!token.getUsername().equals(name)){
//用户名不存在
return null;
//shiro底层会抛出UnKnownAccountException
}
return new SimpleAuthenticationInfo("",password,"");
}
}
分析
- 首先,是认证Authentication,认证发生在用户登录,我们需要在controller中写出他的逻辑:
- 步骤如下:
- 1.获取Subject Subject subject = SecurityUtils.getSubject();
- 2.封装用户数据 UsernamePasswordToken
- 3.执行登录方法 subject.login(token);
@RequestMapping("/login")
public String login(String name ,String password,Model model) {
System.out.println("name="+name);
/**
* 使用Shiro编写认证操作
*/
//1.获取Subject
Subject subject = SecurityUtils.getSubject();
//封装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(name,password);
//执行登录方法
try {
subject.login(token);
//登录成功
//跳转到test.html
return "redirect:/testThymeleaf";
}catch (UnknownAccountException e ){
model.addAttribute("msg","用户名不存在");
System.out.println("UnknownAccountException....................");
return "login";
}catch (IncorrectCredentialsException e ){
model.addAttribute("msg","密码错误");
System.out.println("IncorrectCredentialsException.......................");
return "login";
}
}
认证流程:
认证流程授权流程:
授权流程最后写一点认证拦截
//这里应当注意顺序,上下换一下就算设置了anon还是会被拦截,
//而且目前的顺序会先调用/testThymeleaf,然后再调用login,login还是调用了,只是因为权限是anon所以才没有彖传
//testThymeleaf..........
//login................
filterMap.put("/testThymeleaf","anon");
filterMap.put("/login","anon");
filterMap.put("/*","authc");
//所以个人认为避免全拦截的好,拦截的方统一目录下,在认证拦截的时候只需要拦截这个文件下所有文件的请求
网友评论