先看Shiro的整体架构,这张图来自Shiro的官网。主要包含三个核心组件:Subject, SecurityManager 和 Realms。
Subject:主体,即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。
SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
Authenticator:认证器,管理登陆、登出。
Authorizer:授权器,赋予主体有哪些权限。
Session Manager:Session管理器,Shiro有一套自己的管理机制,不借助任何Web容器情况下使用Session.
Session Dao:Session操作,CRUD。
Cache Manager:缓存角色和权限数据。
Cryptography:加密,快捷方便做加密。
Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
Shiro认证
public class AuthenticationTest {
SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
@Before
public void addUser(){
simpleAccountRealm.addAccount("xiaoqi","123456");
}
@Test
public void testAuthentication(){
//构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(simpleAccountRealm);
//注入SecurityManager
SecurityUtils.setSecurityManager(defaultSecurityManager);
//获取Subject单例对象
Subject subject = SecurityUtils.getSubject();
//使用用户的登录信息创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("xiaoqi","123456");
//登陆
subject.login(token);
System.out.println("isAuthenticated:" + subject.isAuthenticated());
}
}
Shiro授权
public class AuthenticationTest {
SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
@Before
public void addUser(){
simpleAccountRealm.addAccount("xiaoqi","123456","admin");
}
@Test
public void testAuthentication(){
//构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(simpleAccountRealm);
//注入SecurityManager
SecurityUtils.setSecurityManager(defaultSecurityManager);
//获取Subject单例对象
Subject subject = SecurityUtils.getSubject();
//使用用户的登录信息创建令牌
UsernamePasswordToken token = new UsernamePasswordToken("xiaoqi","123456");
//登陆
subject.login(token);
System.out.println("isAuthenticated:" + subject.isAuthenticated());
subject.checkRole("admin");
}
}
自定义Realm
Shiro提供内置的Realm:
/**
* 自定义Realm
*/
public class CustomRealm extends AuthorizingRealm {
Map userMap = new HashMap<>(16);
{
userMap.put("xiaoqi","12345");
super.setName("customRealm");
}
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String userName = (String) principalCollection.getPrimaryPrincipal();
//从数据库或者缓存中获取角色数据
Set roles = getRolesByUserName(userName);
Set permissions = getPermissionByUserName(userName);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setStringPermissions(permissions);
simpleAuthorizationInfo.setRoles(roles);
return simpleAuthorizationInfo;
}
private Set getPermissionByUserName(String userName) {
Set sets = new HashSet<>();
sets.add("user:delete");
sets.add("user:add");
return sets;
}
private Set getRolesByUserName(String userName) {
Set sets = new HashSet<>();
sets.add("admin");
sets.add("users");
return sets;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//1.从主体传过来的认证信息中,获取用户名
String userName = (String) authenticationToken.getPrincipal();
//2.通过用户名到数据库中获取凭证
String password = getPasswordByUserName(userName);
if(password == null){
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("xiaoqi",password,"customRealm");
return authenticationInfo;
}
/**
* 模式数据库查询凭证
* @param userName
* @return
*/
private String getPasswordByUserName(String userName) {
return userMap.get(userName);
}
}
1.配置相关的jar包
org.apache.shiro
shiro-core
1.3.2
org.apache.shiro
shiro-web
1.4.0
org.apache.shiro
shiro-ehcache
${shiro.version}
org.apache.shiro
shiro-spring
${shiro.version}
net.sf.ehcache
ehcache-core
2.6.8
org.slf4j
slf4j-jdk14
1.7.25
2.添加Shiro配置
@Configuration
public class ShiroConfig {
/**
* 配置核心安全事务管理器
* @param shiroRealm
* @return
*/
@Bean(name="securityManager")
public DefaultWebSecurityManager securityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm) {
System.err.println("--------------shiro已经加载----------------");
DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
manager.setRealm(shiroRealm);
return manager;
}
/**
* 过滤器
* @param manager
* @return
*/
@Bean(name="shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") DefaultWebSecurityManager manager) {
ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
bean.setSecurityManager(manager);
//配置登录的url和登录成功的url
bean.setLoginUrl("/login/Login");
bean.setSuccessUrl("/home/getWebHome");
//配置访问权限 拦截策略以键值对存入map
LinkedHashMap filterChainDefinitionMap=new LinkedHashMap<>();//必须LinkedHashMap
filterChainDefinitionMap.put("/css/**", "anon"); //表示可以匿名访问
filterChainDefinitionMap.put("/**", "authc"); //表示认证之后才可以访问
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return bean;
}
/**
* 配置自定义的权限登录器
* @return
*/
@Bean(name="shiroRealm")
public ShiroRealm authRealm() {
ShiroRealm shiroRealm=new ShiroRealm();
return shiroRealm;
}
/**
* 将生命周期交给springboot管理
* @return
*/
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 强制使用cglib创建代理对象
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator creator=new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
/**
* 核心安全配适器
* @param manager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultWebSecurityManager manager) {
AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(manager);
return advisor;
}
}
3.自定义Realm
public class ShiroRealm extends AuthorizingRealm {
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}
这里与第二部分的自定义Realm是同一内容,就不贴代码了。
4.创建首页
登录界面
5.创建Contoller接收请求
@RequestMapping(value="/subLogin", method=RequestMethod.POST, produces="application/json;charset=utf-8")
@ResponseBody
public String subLogin(User user) {
//获得主体
Subject subject = SecurityUtils.getSubject();
//主体提交请求
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
try {
subject.login(token);
} catch (Exception e) {
//把异常的信息打印出来
return e.getMessage();
}
// 编码方式判断是否具有管理员身份
if (subject.hasRole("admin")) {
return "有admin权限";
}
return "无admin权限";
}
Shiro内置过滤器
rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。
port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString
是你访问的url里的?后面的参数。
perms:例子/admins/user/**=perms[user:add:*],perms参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于
isPermitedAll()方法。
roles:例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如/admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。
anon:例子/admins/**=anon 没有参数,表示可以匿名使用。
authc:例如/admins/user/**=authc表示需要认证才能使用,没有参数
authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证
ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查
这些过滤器分为两组,其中anon,authcBasic,auchc,user是认证过滤器,
perms,roles,ssl,rest,port是授权过滤器。
1.配置相关jar包
org.crazycake
shiro-redis
2.4.2.1-RELEASE
2.配置Shiro会话管理
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
/**
* 配置shiro redisManager
*
* @return
*/
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost(host);
redisManager.setPort(port);
redisManager.setExpire(1800);// 配置过期时间
// redisManager.setTimeout(timeout);
// redisManager.setPassword(password);
return redisManager;
}
/**
* cacheManager 缓存 redis实现
*
* @return
*/
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
/**
* RedisSessionDAO shiro sessionDao层的实现 通过redis
*/
public RedisSessionDAO redisSessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}