Spring+Shiro+Token认证

首先引入Shiro的依赖包

   <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.3.2</version>
    </dependency>

在我们的wed.xml中加入我们的shiro过滤器

		 <filter>
    	<filter-name>shiroFilter</filter-name>
    	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    
    <filter-mapping>
    	<filter-name>shiroFilter</filter-name>
    	<url-pattern>/*</url-pattern>
    </filter-mapping>

在Spring配置Shiro中的文件

<?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: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/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"
		>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager"></property>
		<!-- 配置当权限不足的时候返回的路径 -->
		<property name="loginUrl" value="/login/notLoggedIn.do"></property> 
		<property name="unauthorizedUrl" value="/login/lackOfAuthority.do"></property>
	  	<property name="filterChainDefinitions">
	  	<!-- 在value中为访问的路径加入过滤器来作为权限的拦截,可以放shiro中默认的过滤器,也可以自己写过滤器放进去 -->
			<value>
			<!-- anon过滤器:无参,开放权限,可以理解为匿名用户或游客 -->
				/login/auth.do=anon
				<!-- aesToken是本人设计的一个token登陆认证的机制的过滤器,permissionOr是本人设计的一个权限过滤器,而中括号里面的就是访问需要的权限  -->
				/worker/**=aesToken,permissionOr[admin:staff_worker,admin:staff]
				/admin/**=aesToken,permissionOr[admin:staff_admin,admin:staff]
				/payConfig/**=aesToken
				/login/out.do=logout
				/**/**=aesToken,user
			</value>
		</property>
		<property name="filters">
		<!-- 把自己写的过滤器放进map中 -->
			 <map>
	             <entry key="aesToken" value-ref="aesToken"/>
	             <entry key="permissionOr" value-ref="PermissionOrFilter"/>
	             <entry key="rolesOr" value-ref="RolesOrFilter"/>
	        </map>
		</property>
	</bean>

	<!-- 自定义filter过滤器 -->
	<bean id="aesToken" class="com.southgis.iznaer.shiro.filter.AesFilter" />	
		<!-- 自定义filter过滤器 -->
	<bean id="RolesOrFilter" class="com.southgis.iznaer.shiro.filter.RolesOrFilter" />
			<!-- 自定义filter过滤器 -->
	<bean id="PermissionOrFilter" class="com.southgis.iznaer.shiro.filter.PermissionOrFilter" />
	
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="realm"></property>
		<property name="subjectDAO" ref="subjectDAO"></property>
	</bean>

	<bean id="subjectDAO" class="org.apache.shiro.mgt.DefaultSubjectDAO">
		<property name="sessionStorageEvaluator" ref="defaultSessionStorageEvaluator"></property>
	</bean>
		<bean id="defaultSessionStorageEvaluator" class="org.apache.shiro.mgt.DefaultSessionStorageEvaluator">
		<property name="sessionStorageEnabled" value="false"></property>
	</bean>
	<bean id="realm" class="com.southgis.iznaer.shiro.realm.UserRealm">
	</bean>
	
<!-- 开启AoP -->
	<aop:config proxy-target-class="true"/>
	
	<!-- 保证 Shiro内部生命周期 -->
	<bean class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>
	
	<!-- 开启Shiro授权生效 -->
	<bean id="" class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"></bean>
</beans>

自定义token类

import org.apache.shiro.authc.AuthenticationToken;
/**
 * Shiro中的Authentication,主要是检验token时使用
 */
public class AesToken implements AuthenticationToken{
	String token;
public AesToken(String token){
	this.token=token;
}
	public Object getPrincipal() {
		// TODO Auto-generated method stub
		return token;
	}

	public Object getCredentials() {
		// TODO Auto-generated method stub
		return token;
	}

}

创建自定义的shiro过滤器,当过滤器return true时返回到shiro权限认证的自定义realm中,把token放在头部,如果检查到没有token的话则视为未登录状态。有token的情况下会根据token来进行用户的权限认证。

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.json.JSONObject;
import com.southgis.iznaer.base.ResponseConstant;
import com.southgis.iznaer.base.ResponseResult;
import com.southgis.iznaer.util.AesToken;

/**
 * 通过改过滤器跳到Realm中认证用户信息和权限信息
 * @author gyc
 * @date 2018-10-18
 */
public class AesFilter extends BasicHttpAuthenticationFilter  {
	/**
	 * 过滤方法
	 */
	protected boolean isAccessAllowed(ServletRequest request,
			ServletResponse response, Object object)  throws AuthenticationException  {
		String token=((HttpServletRequest) request).getHeader("token");
		ResponseResult jsonResult = new ResponseResult();
		//判断请求的请求头是否带上 "Token"
        if (token!=null) {
            try { 
            	AesToken m=new AesToken(token);
            	getSubject(request, response).login(m);
            	 return true;
            } catch (AuthenticationException e) {
                //token 错误
          	e.printStackTrace();
			jsonResult.setCode(ResponseConstant.TOKEN_FAIL_CODE);//返回失败的code
			jsonResult.setState(ResponseConstant.FAIL_STATE);//返回失败的state
			jsonResult.setDescription("token错误,或token已过期");
			jsonResult.setResults(null);
            JSONObject jsonObject=new JSONObject(jsonResult);
            responseOutWithJson(response,jsonObject.toString());
            return false;
            }
        }
        //如果请求头不存在 Token,则可能是执行登陆操作或者是游客状态访问,无需检查 token,直接返回 true
        return true;
	}
    /** 
     * 以JSON格式输出 
     * @param response 
     */  
    protected void responseOutWithJson(ServletResponse response,  
            Object responseObject) {  
        //将实体对象转换为JSON Object转换  
     //   JSONObject responseJSONObject = JSONObject.fromObject(responseObject);  
    	 HttpServletResponse httpServletResponse = (HttpServletResponse) response;
    	 httpServletResponse.setCharacterEncoding("UTF-8");  
    	 httpServletResponse.setContentType("application/json; charset=utf-8");  
        PrintWriter out = null;  
        try {  
            out = httpServletResponse.getWriter();  
            out.append((String)responseObject);  
        } catch (IOException e) {  
            e.printStackTrace();  
        } finally {  
            if (out != null) {  
                out.close();  
            }  
        }  
    } 
    
}

以下两个也是自定的权限过滤器,功能类似shiro中的默认权限。

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;

/**
 * 自定义权限过滤器,拥有多权限之一的用户可以通过
 * @author gyc
 * @date 2018-10-18
 */
public class PermissionOrFilter extends AuthorizationFilter {

	@Override
	protected boolean isAccessAllowed(ServletRequest req,
			ServletResponse resp, Object object) throws Exception {
		Subject subject = getSubject(req, resp);
		String[] permissions = (String[]) object;
		if (permissions == null || permissions.length == 0) {
			return true;
		}
		for (String permission : permissions) {
			if (subject.isPermitted(permission)) {
				return true;
			}
		}
		return false;
	}

}

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
/**
 * 自定义角色过滤器,拥有多角色之一的用户可以通过
 * @author gyc
 * @date 2018-10-18
 */
public class RolesOrFilter extends AuthorizationFilter {

	@Override
	protected boolean isAccessAllowed(ServletRequest req,
			ServletResponse resp, Object object) throws Exception {
		
		Subject subject = getSubject(req, resp);
		
		String[] roles = (String[]) object;
		
		if (roles == null || roles.length == 0) {
			return true;
		}

		for (String role : roles) {
			if (subject.hasRole(role)) {
				
				return true;
			}
		}
		return false;
	}

}

自定义Reaml,在上文中我们所设置的自定义的shiro过滤器AesFilter 通过后会跳到Reaml中根据我们的token来进行权限的认证和分配。

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Resource;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

public class UserRealm extends AuthorizingRealm {
	User user=null;
	@Resource(name = "userServiceImpl")
	UserService userService;
	@Resource(name = "permissionServiceImpl")
	PermissionService permissionService;
	/**
	 * 必须重写此方法,不然会报错
	 */
	@Override
	public boolean supports(AuthenticationToken token) {
		return token instanceof AesToken;
	}
	/**
     * 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。
     */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException{
		   System.out.println("————身份认证方法————");
	        String token = (String) authenticationToken.getCredentials();
	        user=userService.findUserByToken(token);
    		if(this.user== null){
    			throw new AuthenticationException("token认证失败!");
    		}
    		return new SimpleAuthenticationInfo(token, token, getName());
		}
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		System.out.println("————权限认证————");
               permissionService.findPermissionByUser(user);
		Set<String> permissionSet = new HashSet<String>();
		for (Permission permission : permissions) {
			permissionSet.add(permission.getName());
		}
		info.setStringPermissions(permissionSet);
		//把permissionSet放入info,info成功return后就可以成功配置和认证权限
		return info;
	}
}

到这里我们的shiro配置算是配置完毕了。
配置完以后我们可以到刚刚配置关于shiro那一段的spring xml文件配置我们访问的路径所需要的权限。如下:

	  	<property name="filterChainDefinitions">
	  	<!-- 在value中为访问的路径加入过滤器来作为权限的拦截,可以放shiro中默认的过滤器,也可以自己写过滤器放进去 -->
			<value>
			<!-- anon过滤器:无参,开放权限,可以理解为匿名用户或游客 -->
				/login/auth.do=anon
				<!-- aesToken是本人设计的一个token登陆认证的机制的过滤器,permissionOr是本人设计的一个权限过滤器,而中括号里面的就是访问需要的权限  -->
				/worker/**=aesToken,permissionOr[admin:staff_worker,admin:staff]
				/admin/**=aesToken,permissionOr[admin:staff_admin,admin:staff]
				/payConfig/**=aesToken
				/login/out.do=logout
				/**/**=aesToken,user
			</value>
		</property>

除了可以加入我们自定义的过滤器以外,我们还可以添加shiro默认的过滤器。

Filter解释
anon无参,开放权限,可以理解为匿名用户或游客
authc无参,需要认证
logout无参,注销,执行后会直接跳转到shiroFilterFactoryBean.setLoginUrl(); 设置的 url
authcBasic无参,表示 httpBasic 认证
user无参,表示必须存在用户,当登入操作时不做检查
ssl无参,表示安全的URL请求,协议为 https
perms[user]参数可写多个,表示需要某个或某些权限才能通过,多个参数时写 perms[“user, admin”],当有多个参数时必须每个参数都通过才算通过
roles[user]参数可写多个,表示是某个或某些角色才能通过,多个参数时写 roles[“admin,user”],当有多个参数时必须每个参数都通过才算通过
rest[user]根据请求的方法,相当于 perms[user:method],其中 method 为 post,get,delete 等
port[8081]当请求的URL端口不是8081时,跳转到schemal://serverName:8081?queryString 其中 schmal 是协议 http 或 https 等等,serverName 是你访问的 Host,8081 是 Port 端口,queryString 是你访问的 URL 里的 ? 后面的参数

配置完后当用户访问的时候,访问的流程如下:

  1. AesFilter:判断用户是否带有token,若有进入Reaml
  2. UserRealm :通过token认证获取到User用户
  3. UserRealm :通过User用户拿到权限,进行权限认证。
  4. 通过如perms[user] 、permissionOr[admin:staff_worker]等过滤器。
  5. 成功访问地址
  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值