shiro簡介
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
主要功能
三个核心组件:Subject, SecurityManager 和 Realms.
Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。
Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。
pom配置
直接上代碼
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
shiro配置
(1)编写shiro配置类(基本结构)
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
/**
* shiro配置类
*/
@Configuration
public class ShiroConfiguration {
@SuppressWarnings("unused")
private final static Logger logger = LoggerFactory
.getLogger(ShiroRealm.class);
/**
* LifecycleBeanPostProcessor,这是个DestructionAwareBeanPostProcessor的子类,
* 负责org.apache.shiro.util.Initializable类型bean的生命周期的,初始化和销毁。
* 主要是AuthorizingRealm类的子类,以及EhCacheManager类。
*/
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* HashedCredentialsMatcher,这个类是为了对密码进行编码的, 防止密码在数据库里明码保存,当然在登陆认证的时候,
* 这个类也负责对form里输入的密码进行编码。
*/
@Bean(name = "hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("MD5");
credentialsMatcher.setHashIterations(2);
credentialsMatcher.setStoredCredentialsHexEncoded(true);
return credentialsMatcher;
}
// /**
// * EhCacheManager,缓存管理,用户登陆成功后,把用户信息和权限信息缓存起来,
// * 然后每次用户请求时,放入用户的session中,如果不设置这个bean,每个请求都会查询一次数据库。
// */
// @Bean(name = "ehCacheManager")
// @DependsOn("lifecycleBeanPostProcessor")
// public EhCacheManager ehCacheManager() {
// return new EhCacheManager();
// }
/**
* ShiroFilterFactoryBean,是个factorybean,为了生成ShiroFilter。
* 它主要保持了三项数据,securityManager,filters,filterChainDefinitionManager。
*/
//添加shiro内置过滤器,实现权限相关的url拦截
/**
* 常见过滤器:
* anon:无需认证(登录)可以访问
* authc:必须认证才可以访问
* user:如果使用Remember Me的功能,可以直接访问
* perms:该资源必须得到资源权限才可以访问
* role:该资源必须得到角色权限才可以访问
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager());
// Map<String, Filter> filters = new LinkedHashMap<>();
// LogoutFilter logoutFilter = new LogoutFilter();
// logoutFilter.setRedirectUrl("/login");
// shiroFilterFactoryBean.setFilters(filters);
// authc
Map<String, String> filterChainDefinitionManager = new LinkedHashMap<String, String>();
filterChainDefinitionManager.put("/index", "anon");
filterChainDefinitionManager.put("/login", "anon");
filterChainDefinitionManager.put("/logto", "anon");
// filterChainDefinitionManager.put("/user/edit/**",
// "authc,perms[user:edit]");
// 这里为了测试,固定写死的值,也可以从数据库或其他配置中读取,此处是用权限控制
**//add和update都做了用戶權限攔截**
**filterChainDefinitionManager.put("/add", "perms[user:add]");
filterChainDefinitionManager.put("/update", "perms[user:update]");**
filterChainDefinitionManager.put("/**", "authc");
shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setUnauthorizedUrl("/login");
shiroFilterFactoryBean
.setFilterChainDefinitionMap(filterChainDefinitionManager);
return shiroFilterFactoryBean;
}
/**
* SecurityManager,权限管理,这个类组合了登陆,登出,权限,session的处理,是个比较重要的类。 //
*/
@Bean(name = "securityManager")
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroRealm());
// securityManager.setCacheManager(ehCacheManager());
return securityManager;
}
/**
* ShiroRealm,这是个自定义的认证类,继承自AuthorizingRealm,
* 负责用户的认证和权限的处理,可以参考JdbcRealm的实现。
*/
@Bean(name = "shiroRealm")
@DependsOn("lifecycleBeanPostProcessor")
public ShiroRealm shiroRealm() {
ShiroRealm realm = new ShiroRealm();
// realm.setCredentialsMatcher(hashedCredentialsMatcher());
return realm;
}
/**
* DefaultAdvisorAutoProxyCreator,Spring的一个bean,由Advisor决定对哪些类的方法进行AOP代理。
*/
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
/**
* AuthorizationAttributeSourceAdvisor,shiro里实现的Advisor类,
* 内部使用AopAllianceAnnotationsAuthorizingMethodInterceptor来拦截用以下注解的方法。
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor aASA = new AuthorizationAttributeSourceAdvisor();
aASA.setSecurityManager(securityManager());
return aASA;
}
}
(2)自定义Realm类
import java.util.HashSet;
import java.util.Set;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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.beans.factory.annotation.Autowired;
import com.foxconn.service.UserService;
public class ShiroRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
/**
* 执行授權逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) SecurityUtils.getSubject().getPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set<String> stringSet = new HashSet();
//給用戶寫死的update權限,這裡可以點擊添加和更新來查看區別
**stringSet.add("user:update");**
info.setStringPermissions(stringSet);
return info;
}
/**
* 执行认证逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("-------身份认证方法--------");
String userName = (String) authenticationToken.getPrincipal();
String userPwd = new String((char[]) authenticationToken.getCredentials());
//根据用户名从数据库获取密码
String paw = userService.getUserPaw(userName);
if (userName == null) {
throw new AccountException("用户名不正确");
} else if (!userPwd.equals(paw )) {
throw new AccountException("密码不正确");
}
return new SimpleAuthenticationInfo(userName, userPwd,getName());
}
}
頁面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>index首頁</title>
</head>
<body>
<div shiro:hasPermission="user:add">
進入用戶添加功能:<a href="add">用戶添加</a>
</div>
<div shiro:hasPermission="user:update">
進入用戶更新功能:<a href="update">用戶更新</a>
</div>
</body>
</html>
上圖中,點擊用戶添加和用戶更新做了頁面攔截,如果未登錄,點擊之後跳轉登錄頁面,
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Insert title here</title>
</head>
<body>add成功
</body>
</html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Insert title here</title>
</head>
<body>update成功
</body>
</html>
controller
@Controller
public class LoginController {
@SuppressWarnings("unused")
private Logger logger = Logger.getLogger(this.getClass());
@RequestMapping("/login")
public String login() {
return "login";
}
@RequestMapping(value = "/logto", method = RequestMethod.POST)
public String logto(String username, String password, HttpSession session,
Model model) {
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
username, password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(usernamePasswordToken); // 完成登录
subject.getPrincipal();
return "index";
} catch (Exception e) {
model.addAttribute("msg", "用户名或密码错误");
return "login";// 返回登录页面
}
}
@RequestMapping(value = "/unauto")
public String unauto(HttpServletRequest httpServletRequest) {
return "unauto";
}
@RequestMapping("/logout")
public String logOut(HttpSession session) {
Subject subject = SecurityUtils.getSubject();
subject.logout();
session.removeAttribute("user");
return "login";
}
}
@RequestMapping("/add")
public String add() {
return "add";
}
@RequestMapping("/update")
public String update() {
return "update";
}
@RequestMapping("/index")
public String index() {
return "index";
}