shiro笔记

shiro和spring整合, applicationContext-shiro.xml (摘自springrain项目):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx.xsd 
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd"
    default-lazy-init="false">

    <!-- 权限管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!-- 数据库认证的实现 org.springrain.frame.shiro.ShiroDbRealm -->
        <property name="realm" ref="shiroDbRealm" />
        <!-- session 管理器 -->
        <property name="sessionManager" ref="sessionManager" />
        <!-- 缓存管理器 -->
        <property name="cacheManager" ref="shiroCacheManager" />
    </bean>
    <!-- session管理器 -->
    <bean id="sessionManager"
        class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <!-- URL重写中去掉jsessionId -->
        <property name="sessionIdUrlRewritingEnabled"  value="false"  />        
        <!-- 超时时间 -->
        <property name="globalSessionTimeout" value="${cache.timeout}000" />

        <!-- 定时检查失效的session,默认true -->
        <property name="sessionValidationSchedulerEnabled" value="true" />
        <!-- 删除过期的session,默认true -->
        <property name="deleteInvalidSessions" value="true" />
        <!-- 相隔多久检查一次session的有效性,使用默认的60分钟  -->
        <!--  
        <property name="sessionValidationInterval" value="${cache.timeout}000" />
        -->
        <!-- session存储的实现 -->
        <property name="sessionDAO" ref="shiroSessionDao" />
        <!-- sessionIdCookie的实现,用于重写覆盖容器默认的JSESSIONID -->
        <property name="sessionIdCookie" ref="shareSessionCookie" />

    </bean>

    <!-- sessionIdCookie的实现,用于重写覆盖容器默认的JSESSIONID -->
    <bean id="shareSessionCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <!-- cookie的name,对应的默认是 JSESSIONID -->
        <constructor-arg name="name" value="SHAREJSESSIONID" />
        <!-- jsessionId的path为 / 用于多个系统共享jsessionId -->
        <property name="path" value="/" />
        <!-- more secure, protects against XSS attacks -->
        <property name="httpOnly" value="true" />

    </bean>
    <!-- session存储的实现 -->
    <bean id="shiroSessionDao"
        class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO" />

    <!-- 单机session -->

    <bean id="shiroCacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />

    <!-- shiro的主过滤器,beanId 和web.xml中配置的filter name需要保持一致 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"
        depends-on="frameperms">
        <!-- 安全管理器 -->
        <property name="securityManager" ref="securityManager" />
         <property name="loginUrl" value="/system/login"/>
         <property name="unauthorizedUrl" value="/unauth"/>

        <!-- 访问地址的过滤规则,从上至下,从左至右的优先级,如果有匹配的规则,就会返回,不会再进行匹配 -->
        <property name="filterChainDefinitions">
            <value>
            /test/**=anon
                /layui/** = noSessionCreation
                /js/** =  noSessionCreation
                /css/** = noSessionCreation
                /images/** = noSessionCreation
                /upload/**=anon
                /ajax/**=user
                <!-- 拥有teacher角色才能访问 -->
                /teacher/** = firewall,teacherweblogin,roles[teacher]
                <!-- 拥有admin:edit权限才能访问 -->
                /permit/edit = firewall, perms[admin:edit]
                /index = firewall,user,keepone
                /system/index =firewall,systemuser,framefwlog,keepone
                /logout = firewall,frontuser
                /system/logout = firewall,frontuser
                /system/**=firewall,systemuser,framefwlog,keepone
                /** = firewall,frontuser,framefwlog,keepone,frameperms
            </value>
        </property>
        <!-- 声明自定义的过滤器 -->
        <property name="filters">
            <map>
                <!-- 访问日志记录的过滤器 -->
                <entry key="framefwlog" value-ref="framefwlog"></entry>
                <!--权限校验的过滤器 -->
                <entry key="frameperms" value-ref="frameperms"></entry>
                 <!-- 前台用户过滤器 -->
                <entry key="frontuser" value-ref="frontuser"></entry>

                 <!-- 后台用户过滤器 -->
                <entry key="systemuser" value-ref="systemuser"></entry>
                <!-- 踢出上个账户的过滤器 -->
                <entry key="keepone" value-ref="keepone"/>
                 <!-- 静态化 过滤器 -->
                <entry key="statichtml" value-ref="statichtml"/>

                 <!-- 防火墙 过滤器 -->
                <entry key="firewall" value-ref="firewall"/>

                 <!-- 学校项目登录过滤器 -->
                <entry key="teacherweblogin" value-ref="teacherweblogin"></entry>

            </map>
        </property>
    </bean> 
</beans>

shiro默认过滤器:

这里写图片描述
user表示用户不一定需要已经通过认证,只需要曾经被shiro记住过登录状态的用户就可以正常发起/home请求,就是前面提到的rememberMe

   <!--
       anon:例子/admins/**=anon 没有参数,表示可以匿名使用。

       authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数

       roles:例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。

       perms:例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。

       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里的?后面的参数。

       authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证

       ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https

       user:例如/admins/user/**=user没有参数, 和auth的区别就是如果用户选择rememberme,则下次可以直接访问,登入操作时不做检查

       注:anon,authcBasic,auchc,user是认证过滤器,

       perms,roles,ssl,rest,port是授权过滤器  
       -->

realm的简单实现:

package com.concom.security.infrastructure.shiro;

import java.util.Set;

import org.apache.commons.lang.StringUtils;
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.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.concom.security.application.user.UserService;
import com.concom.security.domain.user.Role;
import com.concom.security.domain.user.User;

public class ShiroRealm extends AuthorizingRealm{

    private final static Logger LOG = LoggerFactory.getLogger(ShiroRealm.class);

    public final static String REALM_NAME = "ShiroCasRealm";

    @Autowired
    private UserService userService;

    public ShiroRealm() {
        setName(REALM_NAME); // This name must match the name in the User
                                // class's getPrincipals() method
    //  setCredentialsMatcher(new Sha256CredentialsMatcher());
    }

    /**
     * 认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {

        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        String username = token.getUsername();
        if(LOG.isTraceEnabled()){
            LOG.trace("开始认证 "+ username);
        }
        try {
            if(StringUtils.isBlank(username)){
                throw new AccountException("can not handle this login");
            }
            User user = userService.getByUsername(username);
            checkUser(user, username);
            return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
        } catch (Exception e) {
            throw translateAuthenticationException(e);
        }
    }

    /**
     * 授权
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        String username = (String)getAvailablePrincipal(principals);

        if(LOG.isTraceEnabled()){
            LOG.trace("开始授权 "+ username);
        }

        User user = userService.getByUsername(username);

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Set<String> rolesAsString = user.getRolesAsString();
        info.addRoles(rolesAsString);
        if(user.hasAuths()){
            info.addStringPermissions(user.getAuthAsString());
        }
        for(Role role : user.getRoles()){
            info.addStringPermissions(role.getAuthsAsString());
        }
        return info;
    }

    /**
     * 异常转换
     * @param e
     * @return
     */
    private AuthenticationException translateAuthenticationException(Exception e) {
        if (e instanceof AuthenticationException) {
            return (AuthenticationException) e;
        }
        if(e instanceof DisabledAccountException){
            return (DisabledAccountException)e;
        }
        if(e instanceof UnknownAccountException){
            return (UnknownAccountException)e;
        }
        return new AuthenticationException(e);
    }
    /**
     * 检查用户
     * @param user
     * @param username
     */
    private void checkUser(User user,String username){
        if(null == user){
            throw new UnknownAccountException(username + " can not find "+username+" from system");
        }
        if(user.isLocked()){
            throw new DisabledAccountException("the account is locked now");
        }
    }

}

web用户登录核心代码:

Subject currUser = SecurityUtils.getSubject();
// user对象是对用户从页面传递过来的账号和密码参数的封装
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword(), rememberMe);
currUser .login(token); 

微信用户模拟自动登录:

// 模拟登陆
User user = new User();
user.setId("用户id");
user.setAccount("用户账号");

SimplePrincipalCollection principals = new SimplePrincipalCollection(user,
        "ShiroRealm");

WebSubject.Builder builder = new WebSubject.Builder(request, response);

builder.principals(principals);
builder.authenticated(true);

WebSubject subject = builder.buildWebSubject();
ThreadContext.bind(subject);

自定义过滤器:

我们可以继承以上shiro默认过滤器, 重写其方法来实现自定义过滤器; 也可以新建过滤器继承AccessControlFilter抽象类, 重写isAccessAllowed()和onAccessDenied()方法. 还可以继承他们的父类: OncePerRequestFilter.

OncePerRequestFilter抽象类:

自定义过滤器可以继承shiro默认过滤器和AccessControlFilter, 还可以继承他们的父类: OncePerRequestFilter, 重写doFilterInternal()方法即可.
顾名思义: 一次请求只经过一次的过滤器.
shiro过滤器都默认继承此过滤器.
话说回来, 过滤器不都是只经过一次吗? 不一定, servlet版本不同,表现也不同:

在servlet-2.3中,Filter会过滤一切请求,包括服务器内部使用forward转发请求和<%@ include file=”/index.jsp”%>的情况。
到了servlet-2.4中Filter默认下只拦截外部提交的请求,forward和include这些内部转发都不会被过滤,但是有时候我们需要 forward的时候也用到Filter。

shiro工具类WebUtils

 常用API:

这里写图片描述

Subject获取及使用

1.获取Subject:
Subject subject = SecurityUtils.getSubject();
2.Subject对象常用API:

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值