Shiro安全框架

一、Shiro整体架构

先看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认证、授权、自定义Realm

Shiro认证

  1. 构建SecurityManager环境
  2. 主题提交认证请求
  3. SecurityManager认证
  4. Authenticator认证
  5. Realm验证
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授权

  1. 构建SecurityManager环境
  2. 主题提交授权请求
  3. SecurityManager授权
  4. Authorizer授权
  5. Realm获取角色权限数据

 

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:

  • IniRealm
  • JdbcRealm
/**
 * 自定义Realm
 */
public class CustomRealm extends AuthorizingRealm {

    Map<String,String> 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<String> roles = getRolesByUserName(userName);
        Set<String> permissions = getPermissionByUserName(userName);
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.setStringPermissions(permissions);
        simpleAuthorizationInfo.setRoles(roles);
        return simpleAuthorizationInfo;
    }

    private Set<String> getPermissionByUserName(String userName) {
        Set<String> sets = new HashSet<>();
        sets.add("user:delete");
        sets.add("user:add");
        return sets;
    }

    private Set<String> getRolesByUserName(String userName) {
        Set<String> 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);
    }
}

三、Shiro集成Spring

1.配置相关的jar包

    <dependencies>        
        <!--核心包shiro-core-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!--Web相关包shiro-web-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.4.0</version>
        </dependency>
        <!--缓存包shiro-ehcache-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <!--缓存包shiro-ehcache-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <!--Ehcache缓存核心包ehcache-core-->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache-core</artifactId>
            <version>2.6.8</version>
        </dependency>
        <!--Shiro自身日志包slf4j-jdk14-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-jdk14</artifactId>
            <version>1.7.25</version>
        </dependency>
    </dependencies>

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<String, String> 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.创建首页

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title>登录界面</title>
	</head>
	<body>
		<form action="subLogin" method="post">
			用户名:<input type="text" name="username"><br>
			密    码:<input type="password" name="password"><br>
			<input type="submit" value="登录"> 
		</form>
	</body>
</html>

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是授权过滤器。

四、Shiro会话管理和缓存管理

1.配置相关jar包

      <!-- shiro+redis缓存插件 -->
      <dependency>
        <groupId>org.crazycake</groupId>
        <artifactId>shiro-redis</artifactId>
        <version>2.4.2.1-RELEASE</version>
      </dependency>

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;
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值