目录
shiro
1、简介
1.1、什么是shiro
- Apache Shiro 是 Java 的一个安全(权限)框架
- Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境
1.2、功能
Authentication
:认证、验证Authorization
:授权Session Management
:会话管理Crytography
:加密机制- …
1.3、shiro架构
Subject
- 应用代码直接交互的对象是Subject,也就是说Shiro的对外API 核心就是Subject
- Subject 代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等
- Subject 其实是一个门面,SecurityManager才是实际的执行者
- 与Subject 的所有交互都会委托给SecurityManager
SecurityManager
- 安全管理器
- 所有与安全有关的操作都会与SecurityManager交互,且其管理着所有Subject
- 它是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于SpringMVC中DispatcherServlet的角色
Realm
- Shiro从Realm 获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm 获取相应的用户进行比较以确定用户身份是否合法
- 也需要从Realm 得到用户相应的角色/权限进行验证用户是否能进行操作,可以把Realm 看成DataSource
总结:
- 应用代码通过Subject来进行认证和授权,而Subject又委托给SecurityManager
- 我们需要给Shiro的SecurityManager注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断
2、案例分析
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Quickstart {
private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
public static void main(String[] args) {
// 工厂模式,通过shiro.ini 配置文件中的信息,生成一个工厂实例
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
// 通过SecurityUtils 获取当前执行的用户Subject:
Subject currentUser = SecurityUtils.getSubject();
//通过当前用户拿到Shiro的Session 可以脱离web存值取值
Session session = currentUser.getSession();
// 使用session存值取值
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("Retrieved the correct value! [" + value + "]");
}
// 让我们登录当前用户,以便我们可以检查角色和权限:
// 这里和SpringSecurity 使用了类似的代码,判断用户是否被认证
if (!currentUser.isAuthenticated()) {
//如果被认证,就可以获得一个令牌(token )
//通过用户的账号密码生成一个令牌
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
//设置记住我功能
token.setRememberMe(true);
try {
//执行登录操作
currentUser.login(token);
} catch (UnknownAccountException uae) {
//如果用户名不存在
log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
// 如果密码不正确
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
//用户被锁定,如密码输出过多,则被锁住
log.info("The account for username " + token.getPrincipal() + " is locked. " +"Please contact your administrator to unlock it.");
}
// ... 在此处捕获更多异常
catch (AuthenticationException ae) {
//意外情况?错误?
}
}
//currentUser些用法
//打印其标识主体(在这种情况下,为用户名) :
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
// 检查角色是否存在
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
//粗粒度
if (currentUser.isPermitted("lightsaber:wield")) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
//细粒度
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'." +"Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}
//注销
currentUser.logout();
//结束
System.exit(0);
}
}
主要流程:
//获取当前对象
Subject currentUser = SecurityUtils.getSubject();
//根据当前对象获取对应的Session
Session session = currentUser.getSession();
//判断用户是否被认证,根据用户名和密码生成令牌
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
//设置记住我功能
token.setRememberMe(true);
//拿到令牌进行登录
currentUser.login(token);
//打印其标识主体
currentUser.getPrincipal()
//注销
currentUser.logout();
3、动手实践
3.1、自定义Realm配置类
package com.mtf.config;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class Realm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了授权方法!");
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了认证方法!");
return null;
}
}
- 继承
AuthorizingRealm
重写里面的“认证”和“授权”方法
3.2、自定义shiro配置类
package com.mtf.config;
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;
@Configuration
public class ShiroConfig {
@Bean(name = "shiroFilterFactoryBean")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
return shiroFilterFactoryBean;
}
@Bean(name = "defaultWebSecurityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRemal") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
@Bean(name = "userRealm")
public UserRealm getUserRealm(){
return new UserRealm();
}
}
- 执行顺序:UserRealm-》DefaultWebSecurityManager-》ShiroFilterFactoryBean
3.3、认证和授权
请求拦截
//在配置类中添加shiro内置过滤器
//添加shiro内置过滤器
Map<String, String> filterMap = new LinkedHashMap<>();
/*
anon : 无需认证,就可以访问
authc : 必须认证,才能访问
user : 必须拥有 “记住我”功能才能用
perms : 拥有对某个资源的权限才能访问
role : 拥有某个角色权限才能访问
*/
filterMap.put("/functions/*","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
//设置拦截后要跳转的页面,一般是登录页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
用户认证(与数据库交互判断登录的用户名账号密码是否正确)
//Controller
//用户认证
@RequestMapping("/login")
public String Login(String username, String password, Model model){
//获取当前对象
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
token.setRememberMe(true);
try {
if (!subject.isAuthenticated()){
subject.login(token);
return "index";
}
} catch (UnknownAccountException e) {
model.addAttribute("msg","用户名错误!");
return "login";
}catch (IncorrectCredentialsException e){
model.addAttribute("msg","密码错误!");
return "login";
}
return "index";
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了认证方法!");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
User user = userService.queryByName(token.getUsername());
if (user==null){
return null;
}
return new SimpleAuthenticationInfo("",user.getPassword(),"");
}
用户授权
//权限设置
filterMap.put("/functions/*","perms[functions:*]");
//通过关键字段来判断用户权限
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了授权方法!");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Subject subject = SecurityUtils.getSubject();
User user = (User) subject.getPrincipal();
info.addStringPermission(user.getPerms());
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了认证方法!");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
User user = userService.queryByName(token.getUsername());
if (user==null){
return null;
}
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
shiro和thymeleaf整合
// 整合ShiroDialect:用来整合shiro-thymeleaf
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}