您的位置:首页 > 编程语言 > Java开发

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

2016-05-20 09:13 615 查看
</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>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: