shiro + mybatis+ spring (只用shiro的密码校验和并发剔除)——不用权限之类

</pre>          shiro很强大,但往往项目不可能大改造,往往只需要部分功能,比如用到验证码,加密,还有就是同一个账户在两个地方登录,剔除第一个登录者,本文只提供思路和部分代码,<p></p><p></p><p>自定义实现ream,</p><p></p><pre name="code" class="java">package com.shiro.shiro.realm;

import java.util.HashSet;
import java.util.Set;

import org.apache.shiro.authc.*;
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.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import com.shiro.model.User;
import com.shiro.service.UserService;


public class UserRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;
    //获取授权信息
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
     <span style="font-size:18px;">   <span style="color:#ff0000;"><strong>Set<String>  set =new HashSet<String>();
        set.add("*:*:*");</strong></span>
        <span style="color:#ff0000;"><strong>authorizationInfo.setRoles(set);
        authorizationInfo.setStringPermissions(set);
        return authorizationInfo;</strong></span></span>
    }
   //获取身份验证信息
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        String username = (String)token.getPrincipal();

        User user = userService.findByUsername(username);

        if(user == null) {
            throw new UnknownAccountException();//没找到帐号
        }

        if(Boolean.TRUE.equals(user.getLocked())) {
            throw new LockedAccountException(); //帐号锁定
        }

        //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以自定义实现
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user.getUsername(), //用户名
                user.getPassword(), //密码
                ByteSource.Util.bytes(user.getCredentialsSalt()),//salt=username+salt
                getName()  //realm name
        );
        return authenticationInfo;
    }

    @Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }

    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }

    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }

    public void clearAllCachedAuthorizationInfo() {
        getAuthorizationCache().clear();
    }

    public void clearAllCachedAuthenticationInfo() {
        getAuthenticationCache().clear();
    }

    public void clearAllCache() {
        clearAllCachedAuthenticationInfo();
        clearAllCachedAuthorizationInfo();
    }

}

只要在<pre name="code" class="java">doGetAuthorizationInfo方法体里授权所有资源所有角色就好了
</pre><pre name="code" class="java">
剔除功能
</pre><pre name="code" class="java"><pre name="code" class="java">package com.shiro.shiro.filter;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;
import java.util.Deque;
import java.util.LinkedList;

/**
 * 并发剔除
 */
public class KickoutSessionControlFilter extends AccessControlFilter {

    private String kickoutUrl; //踢出后到的地址
    private boolean kickoutAfter = false; //踢出之前登录的/之后登录的用户 默认踢出之前登录的用户
    private int maxSession = 1; //同一个帐号最大会话数 默认1

    private SessionManager sessionManager;
    private Cache<String, Deque<Serializable>> cache;

    public void setKickoutUrl(String kickoutUrl) {
        this.kickoutUrl = kickoutUrl;
    }

    public void setKickoutAfter(boolean kickoutAfter) {
        this.kickoutAfter = kickoutAfter;
    }

    public void setMaxSession(int maxSession) {
        this.maxSession = maxSession;
    }

    public void setSessionManager(SessionManager sessionManager) {
        this.sessionManager = sessionManager;
    }

    public void setCacheManager(CacheManager cacheManager) {
        this.cache = cacheManager.getCache("shiro-kickout-session");
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        Subject subject = getSubject(request, response);
        if(!subject.isAuthenticated() && !subject.isRemembered()) {//未授权且非remember 
            //如果没有登录,直接进行之后的流程
            return true;
        }

        Session session = subject.getSession();
        String username = (String) subject.getPrincipal();
        Serializable sessionId = session.getId();

        //TODO 同步控制
        Deque<Serializable> deque = cache.get(username);
        if(deque == null) {
            deque = new LinkedList<Serializable>();
            cache.put(username, deque);
        }

        //如果队列里没有此sessionId,且用户没有被踢出;放入队列
        if(!deque.contains(sessionId) && session.getAttribute("kickout") == null) {
            deque.push(sessionId);
        }

        //如果队列里的sessionId数超出最大会话数,开始踢人
        while(deque.size() > maxSession) {
            Serializable kickoutSessionId = null;
            if(kickoutAfter) { //如果踢出后者
                kickoutSessionId = deque.removeFirst();
            } else { //否则踢出前者
                kickoutSessionId = deque.removeLast();
            }
            try {
                Session kickoutSession = sessionManager.getSession(new DefaultSessionKey(kickoutSessionId));
                if(kickoutSession != null) {
                    //设置会话的kickout属性表示踢出了
                    kickoutSession.setAttribute("kickout", true);
                }
            } catch (Exception e) {//ignore exception
            }
        }

        //如果被踢出了,直接退出,重定向到踢出后的地址
        if (session.getAttribute("kickout") != null) {
            //会话被踢出了
            try {
                subject.logout();//这里走的shiro的退出
            } catch (Exception e) { //ignore
            }
            saveRequest(request);
            WebUtils.issueRedirect(request, response, kickoutUrl);
            return false;
        }

        return true;
    }
}

此处用到了缓存,当然可以自己实现,把session存到数据库中然后判断操作
 
</pre><pre name="code" class="java">再看下部分配置文件
</pre><pre name="code" class="java">
<pre name="code" class="html"><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 缓存管理器 -->
    <bean id="cacheManager" class="com.shiro.spring.cache.SpringCacheManagerWrapper">
        <property name="cacheManager" ref="springCacheManager"/>
    </bean>
    <!-- 凭证匹配器 -->
    <bean id="credentialsMatcher" class="com.shiro.shiro.credentials.CredentialsMatcher">
        <property name="hashAlgorithmName" value="md5"/>
        <property name="hashIterations" value="2"/>
        <property name="storedCredentialsHexEncoded" value="true"/>
    </bean>
    <!-- Realm实现 -->
    <bean id="userRealm" class="com.shiro.shiro.realm.UserRealm">
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
        <property name="cachingEnabled" value="false"/>
    </bean>
    <!-- 会话ID生成器 -->
    <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
    <!-- 会话Cookie模板 maxAge=-1表示浏览器关闭时失效此Cookie-->
    <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <constructor-arg value="sid"/>
        <property name="httpOnly" value="true"/>
        <property name="maxAge" value="-1"/>
    </bean>
    <!-- 会话DAO -->
    <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
        <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
        <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
    </bean>
    <!-- 会话验证调度器 -->
    <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
        <property name="sessionValidationInterval" value="1800000"/>
        <property name="sessionManager" ref="sessionManager"/>
    </bean>
    <!-- 会话管理器 -->
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <property name="globalSessionTimeout" value="1800000"/>
        <property name="deleteInvalidSessions" value="true"/>
        <property name="sessionValidationSchedulerEnabled" value="true"/>
        <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
        <property name="sessionDAO" ref="sessionDAO"/>
        <property name="sessionIdCookieEnabled" value="true"/>
        <property name="sessionIdCookie" ref="sessionIdCookie"/>
    </bean>
    <!-- 安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="userRealm"/>
        <property name="sessionManager" ref="sessionManager"/>
        <property name="cacheManager" ref="cacheManager"/>
    </bean>
	
    <!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
        <property name="arguments" ref="securityManager"/>
    </bean>

    <!-- 基于Form表单的身份验证过滤器 -->
<strong>    <bean id="authcFilter" class="com.shiro.shiro.filter.MyFormAuthenticationFilter">
        <property name="usernameParam" value="username"/>
        <property name="passwordParam" value="password"/>
        <property name="failureKeyAttribute" value="shiroLoginFailure"/>
    </bean>
    
     <bean id="validateFilter" class="com.shiro.shiro.filter.ValidateFilter">
        <property name="verificationAbled" value="true"/>
        <property name="verificationParam" value="verificationParam"/>
        <property name="failureKeyAttribute" value="shiroLoginFailure"/>
    </bean></strong>
       <!-- Shiro的Web过滤器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/login"/>
        <property name="filters">
            <util:map>
                <entry key="authc" value-ref="<strong>authcFilter</strong>"/>
                <entry key="sysUser" value-ref="sysUserFilter"/>
                <entry key="validateFilter" value-ref="<strong>validateFilter</strong>"/>
                <entry key="kickout" value-ref="<strong>kickoutSessionControlFilter</strong>"/>
            </util:map>
        </property>
        <property name="filterChainDefinitions">
            <value>
                 /login = <strong>validateFilter</strong>,authc
                 /logout = logout
                /authenticated = authc
                /** = <strong>kickout</strong>,user,sysUser
            </value>
        </property>
    </bean>
    
    <!-- currenuser  -->
    <bean id="sysUserFilter" class="com.shiro.shiro.filter.SysUserFilter"/>
    <!-- 并发踢出 
            kickoutAfter:是否踢出后来登录的,默认是false 
            kickoutUrl:被踢出后重定向到的地址
            maxSession:同一个用户最大的会话数,默认1
     -->
    <bean id="kickoutSessionControlFilter" class="com.shiro.shiro.filter.KickoutSessionControlFilter">
        <property name="cacheManager" ref="cacheManager"/>
        <property name="sessionManager" ref="sessionManager"/>
        <property name="kickoutAfter" value="false"/>
        <property name="maxSession" value="2"/>
        <property name="kickoutUrl" value="/login?kickout=1"/>
    </bean>
    <!-- Shiro生命周期处理器-->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    
</beans>


 
</pre><pre name="code" class="java">自定义的
</pre><pre name="code" class="java"><pre name="code" class="java">package com.shiro.shiro.filter;

import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * 自定义shiro FormAuthenticationFilter 表单过滤器
 * @author changliang
 *
 */
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
    /**
     * 授权是否失败
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        if(request.getAttribute(getFailureKeyAttribute()) != null) {
            return true;
        }
        return super.onAccessDenied(request, response, mappedValue);
    }
}
</pre>密码校验<pre>
<pre name="code" class="java">package com.shiro.shiro.filter;

import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;

import com.shiro.service.UserService;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

/**
 * 验证码过滤器
 */
public class ValidateFilter extends AccessControlFilter {
	
	@SuppressWarnings("unused")
	@Autowired
	private UserService userService;

    private boolean verificationAbled = true;                //是否开启验证码支持

    @SuppressWarnings("unused")
	private String verificationParam = "verificationParam";  //前台提交的验证码参数名

    private String failureKeyAttribute = "shiroLoginFailure"; //验证码验证失败后存储到的属性名
    
   
    public void setVerificationAbled(boolean verificationAbled) {
		this.verificationAbled = verificationAbled;
	}

	public void setVerificationParam(String verificationParam) {
		this.verificationParam = verificationParam;
	}

	public void setFailureKeyAttribute(String failureKeyAttribute) {
        this.failureKeyAttribute = failureKeyAttribute;
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        //1、设置验证码是否开启属性,页面可以根据该属性来决定是否显示验证码
        request.setAttribute("verificationAbled", verificationAbled);

        HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
        //2、判断验证码是否禁用 或不是表单提交(允许访问)
        if (verificationAbled == false || !"post".equalsIgnoreCase(httpServletRequest.getMethod())) {
            return true;
        }
       <span style="color:#ff0000;"><strong> //3、此时是表单提交,验证验证码是否正确
        //TODO 增加自己的验证码校验
        //return  userService.verification(verificationParam);
        return true;</strong></span>
    }
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        //如果验证码失败了,存储失败key属性
    	//在LoginController里过滤用到
        request.setAttribute(failureKeyAttribute, "<span style="color:#ff0000;">verification.error</span>");
        return true;
    }
}



处理器
 
</pre><pre name="code" class="java"><pre name="code" class="java">package com.shiro.controller;

import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
/**
 * 常见错误:
 *  subject.login()
	 * DisabledAccountException(禁用的帐号)、
	 * LockedAccountException(锁定的帐号)、
	 * UnknownAccountException(错误的帐号)、
	 * ExcessiveAttemptsException(登录失败次数过多)、
	 * IncorrectCredentialsException (错误的凭证)、
	 * ExpiredCredentialsException(过期的凭证)
 * @author changliang
 *
 */
@Controller
public class LoginController {

    @RequestMapping(value = "/login"    )
    public String showLoginForm(HttpServletRequest req, Model model) {
        String exceptionClassName = (String)req.getAttribute("shiroLoginFailure");
        String error = null;
        if(UnknownAccountException.class.getName().equals(exceptionClassName)) {
            error = "用户名/密码错误";
        } else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
            error = "用户名/密码错误";//错误的凭证
        }else if("<span style="color:#ff0000;">verification.error</span>".equals(exceptionClassName)) {
            error = "验证码错误";
        } else if(exceptionClassName != null) {
            error = "其他错误:" + exceptionClassName;
        }
        model.addAttribute("error", error);
        return "login";
    }


}

参考 :http://jinnianshilongnian.iteye.com/blog/2049092

 
</pre><pre name="code" class="java">
</pre><pre name="code" class="java">
</pre><pre name="code" class="java">
</pre><pre name="code" class="java">
</pre>

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值