设为首页 收藏本站
查看: 908|回复: 0

[经验分享] Shiro 整合SpringMVC 并且实现权限管理,登录和注销

[复制链接]

尚未签到

发表于 2017-3-1 11:19:56 | 显示全部楼层 |阅读模式
  Apache Shiro是Java的一个安全框架。目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。
  因为我总结的是使用SpringMVC和Apache Shiro整合,注重的是整合和使用,至于基础,我这里就不细说了.按照惯例,既然是需要创建项目,那么我们首先需要JAR包,Apache shiro的架包除了除了基本的以外,我们还需要shiro-web和shiro-spring的的架包,下面是所需要的所有shiro架包,至于其他的架包,像缓存的架包,Spring和SpringMVC的架包还是和我们以前使用的架包一样的。



<dependency>  
<groupId>org.apache.shiro</groupId>  
<artifactId>shiro-core</artifactId>  
<version>1.2.3</version>  
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.3</version>
</dependency>
  所有的架包都搞清楚了以后,我们就可以开始正式搭建了,创建一个maven项目,将需要的jar全部映入,上面的只是shiro需要的几个jar,其他的需要自己引入。下面就分步骤来创建
  1.首先创建spring的配置文件,位置都在在resource中,配置文件是spring-context.xml,创建Apache Shiro的配置文件,名字是spring-context-shiro.xml,还有一个配置文件是springmvc的,配置文件是spring-mvc,这样起名是有原因的,因为这样我们就可以在web.xml中设置配置文件的时候,直接使用通配符了:
  



<!-- 配置spring容器的路径 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/spring-context-*.xml</param-value>
</context-param>
<!-- 对spring开始监听 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
  这样就可以扫描到两个配置文件了,又不会扫描到我们的spring-mvc.xml了,
  2除了在web.xml中设置这个以外,我们还需要设置spring-mvc的位置:



<!-- MVC Servlet
设置springmvc的Servlet
-->
<servlet>
<servlet-name>springServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
  3.在web.xml中配置shiroFilter:



     <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>
  注意,这个shiroFilter名称,后面的配置还需要使用到,所以要注意咯。
4,因为shiro的session是自己实现的,所以我们还需要一个缓存框架,所以在spring的配置文件一定要注意配置哦,



    <!-- 缓存 -->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:${ehcache.file}"></property>
</bean>
  spring的其他的配置,该怎样还是这样,我们的重点是配置spring-context-shiro.xml:先把配置的贴出来,然后讲一下这几个配置的意义:



<?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" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-4.0.xsd"
default-lazy-init="true">
<description>Shiro Configuration</description>
<!-- 加载配置属性文件 -->
<context:property-placeholder ignore-unresolvable="true" location="classpath:yonyou.properties" />
<!-- Shiro权限过滤过滤器定义 -->
<bean name="shiroFilterChainDefinitions" class="java.lang.String">
<constructor-arg>
<value>
/static/** = anon
/userfiles/** = anon
${adminPath}/cas = cas
${adminPath}/login = authc
${adminPath}/logout = logout
${adminPath}/** = user
</value>
</constructor-arg>
</bean>
<!-- 安全认证过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" /><!--
<property name="loginUrl" value="${cas.server.url}?service=${cas.project.url}${adminPath}/cas" /> -->
<property name="loginUrl" value="${adminPath}/login" />
<property name="successUrl" value="${adminPath}?login" />
<property name="filters">
<map>
<entry key="cas" value-ref="casFilter"/>
<entry key="authc" value-ref="formAuthenticationFilter"/>
</map>
</property>
<property name="filterChainDefinitions">
<ref bean="shiroFilterChainDefinitions"/>
</property>
</bean>
<!-- CAS认证过滤器 -->  
<bean id="casFilter" class="org.apache.shiro.cas.CasFilter">  
<property name="failureUrl" value="${adminPath}/login"/>
</bean>
<!-- 定义Shiro安全管理配置 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="systemAuthorizingRealm" />
<property name="sessionManager" ref="sessionManager" />
<property name="cacheManager" ref="shiroCacheManager" />
</bean>
<!-- 自定义会话管理配置 -->
<bean id="sessionManager" class="com.yonyou.hotusm.common.security.session.SessionManager">
<property name="sessionDAO" ref="sessionDAO"/>
<!-- 会话超时时间,单位:毫秒  -->
<property name="globalSessionTimeout" value="${session.sessionTimeout}"/>
<!-- 定时清理失效会话, 清理用户直接关闭浏览器造成的孤立会话   -->
<property name="sessionValidationInterval" value="${session.sessionTimeoutClean}"/>
<!--          <property name="sessionValidationSchedulerEnabled" value="false"/> -->
<property name="sessionValidationSchedulerEnabled" value="true"/>
<property name="sessionIdCookie" ref="sessionIdCookie"/>
<property name="sessionIdCookieEnabled" value="true"/>
</bean>
<!-- 指定本系统SESSIONID, 默认为: JSESSIONID 问题: 与SERVLET容器名冲突, 如JETTY, TOMCAT 等默认JSESSIONID,
当跳出SHIRO SERVLET时如ERROR-PAGE容器会为JSESSIONID重新分配值导致登录会话丢失! -->
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg name="name" value="jeesite.session.id"/>
</bean>
<!-- 自定义Session存储容器 -->
<!--     <bean id="sessionDAO" class="com.yonyou.hotusm.common.security.shiro.session.JedisSessionDAO"> -->
<!--         <property name="sessionIdGenerator" ref="idGen" /> -->
<!--         <property name="sessionKeyPrefix" value="${redis.keyPrefix}_session_" /> -->
<!--     </bean> -->
<bean id="sessionDAO" class="com.yonyou.hotusm.common.security.session.CacheSessionDAO">
<property name="sessionIdGenerator" ref="idGen" />
<property name="activeSessionsCacheName" value="activeSessionsCache" />
<property name="cacheManager" ref="shiroCacheManager" />
</bean>
<!-- 定义授权缓存管理器 -->
<!--     <bean id="shiroCacheManager" class="com.thinkgem.jeesite.common.security.shiro.cache.SessionCacheManager" /> -->
<bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManager" ref="cacheManager"/>
</bean>
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- AOP式方法级权限检查  -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true" />
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
</beans>
ecurityManager:是shiro最重要的一个对象,授权和验证都是由它来做的,下面就一一的来讲他的依赖类,
一:realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。下对于源代码,我就不细细的研究了,下面是我重写的realm,:



package com.yonyou.hotusm.module.sys.security;

import java.io.Serializable;

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.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.yonyou.hotusm.module.sys.dao.UserDao;
import com.yonyou.hotusm.module.sys.entity.User;
import com.yonyou.hotusm.module.sys.util.UserUtils;

@Service
public class SystemAuthorizingRealm extends AuthorizingRealm{

@Autowired
private UserDao userDao;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
info.addStringPermission("sys:manager");
info.addStringPermission("user");
System.out.println("开始授权");
return info;
}

@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upToken=(UsernamePasswordToken) token;
String username=upToken.getUsername();
String password=new String(upToken.getPassword());
User user=new User();
user.setLoginName(username);
user=userDao.get(user);
System.out.println("===========");
if(user!=null){
if(user.getPassword().equals(password)){
return new SimpleAuthenticationInfo(username,password,getName());
}
}
throw new UnauthenticatedException();

}
public static class Principal implements Serializable {

private static final long serialVersionUID = 1L;

private String id; // 编号
private String loginName; // 登录名
private String name; // 姓名


public Principal(User user) {
this.id = user.getId();
this.loginName = user.getLoginName();
this.name = user.getName();
}

public String getId() {
return id;
}

public String getLoginName() {
return loginName;
}

public String getName() {
return name;
}

/**
* 获取SESSIONID
*/
public String getSessionid() {
try{
return (String) UserUtils.getSession().getId();
}catch (Exception e) {
return "";
}
}
@Override
public String toString() {
return id;
}
}
}

  看的出来,其中最重要的是doGetAuthorizationInfo和doGetAuthenticationInfo,这两个方法,doGetAuthorizationInfo是对当前的用户进行授权的,至于授权的时期,就是当用户需要验证的时候,我这里只是简单的写死了,但是在实际项目开发中,我们一般会将权限存放在数据表中,所以真实情况是先到数据库中查出一个集合,然后迭代授权,
  doGetAuthenticationInfo对于的是对用户验证,这里我们就需要从数据库中根据用户查出用户,根据用户情况,抛出不用的异常。
下面就是讲解sessionManager,因为Shiro有自己的一套session体系,有sessionManager就不奇怪了,sessionManager主要职责是管理session的创建和删除,特别提一下,sessionManager对session的操作,其实只是调用了sessionDAO,然再加上自己的一些操作,所以,我们可以看到sessionManager的bean还依赖sessionDAO,下面是自己实现的sessionManager:



package com.yonyou.hotsum.common.security.shiro.session;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;

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

import org.apache.shiro.session.InvalidSessionException;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SessionContext;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;

/**
* 自定义WEB会话管理类
* @author hotusm
*
*/
public class SessionManager extends DefaultWebSessionManager {

public SessionManager() {
super();
}

@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
// 如果参数中包含“__sid”参数,则使用此sid会话。 例如:http://localhost/project?__sid=xxx&__cookie=true

String sid = request.getParameter("__sid");
if (org.apache.commons.lang3.StringUtils.isNotBlank(sid)) {
// 是否将sid保存到cookie,浏览器模式下使用此参数。
if (WebUtils.isTrue(request, "__cookie")){
HttpServletRequest rq = (HttpServletRequest)request;
HttpServletResponse rs = (HttpServletResponse)response;
Cookie template = getSessionIdCookie();
Cookie cookie = new SimpleCookie(template);
cookie.setValue(sid); cookie.saveTo(rq, rs);
}
// 设置当前session状态

request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
ShiroHttpServletRequest.URL_SESSION_ID_SOURCE); // session来源与url

request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sid);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return sid;
}else{
return super.getSessionId(request, response);
}
}

@Override
public void validateSessions() {
super.validateSessions();
}

@Override
protected Session retrieveSession(SessionKey sessionKey) {
try{
return super.retrieveSession(sessionKey);
}catch (UnknownSessionException e) {
// 获取不到SESSION不抛出异常
return null;
}
}

@Override
public Date getStartTimestamp(SessionKey key) {
try{
return super.getStartTimestamp(key);
}catch (InvalidSessionException e) {
// 获取不到SESSION不抛出异常
return null;
}
}

@Override
public Date getLastAccessTime(SessionKey key) {
try{
return super.getLastAccessTime(key);
}catch (InvalidSessionException e) {
// 获取不到SESSION不抛出异常
return null;
}
}

@Override
public long getTimeout(SessionKey key){
try{
return super.getTimeout(key);
}catch (InvalidSessionException e) {
// 获取不到SESSION不抛出异常
return 0;
}
}

@Override
public void setTimeout(SessionKey key, long maxIdleTimeInMillis) {
try{
super.setTimeout(key, maxIdleTimeInMillis);
}catch (InvalidSessionException e) {
// 获取不到SESSION不抛出异常

}
}

@Override
public void touch(SessionKey key) {
try{
super.touch(key);
}catch (InvalidSessionException e) {
// 获取不到SESSION不抛出异常

}
}

@Override
public String getHost(SessionKey key) {
try{
return super.getHost(key);
}catch (InvalidSessionException e) {
// 获取不到SESSION不抛出异常
return null;
}
}

@Override
public Collection<Object> getAttributeKeys(SessionKey key) {
try{
return super.getAttributeKeys(key);
}catch (InvalidSessionException e) {
// 获取不到SESSION不抛出异常
return null;
}
}

@Override
public Object getAttribute(SessionKey sessionKey, Object attributeKey) {
try{
return super.getAttribute(sessionKey, attributeKey);
}catch (InvalidSessionException e) {
// 获取不到SESSION不抛出异常
return null;
}
}

@Override
public void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) {
try{
super.setAttribute(sessionKey, attributeKey, value);
}catch (InvalidSessionException e) {
// 获取不到SESSION不抛出异常

}
}

@Override
public Object removeAttribute(SessionKey sessionKey, Object attributeKey) {
try{
return super.removeAttribute(sessionKey, attributeKey);
}catch (InvalidSessionException e) {
// 获取不到SESSION不抛出异常
return null;
}
}

@Override
public void stop(SessionKey key) {
try{
super.stop(key);
}catch (InvalidSessionException e) {
// 获取不到SESSION不抛出异常

}
}

@Override
public void checkValid(SessionKey key) {
try{
super.checkValid(key);
}catch (InvalidSessionException e) {
// 获取不到SESSION不抛出异常

}
}

@Override
protected Session doCreateSession(SessionContext context) {
try{
return super.doCreateSession(context);
}catch (IllegalStateException e) {
return null;
}
}

@Override
protected Session newSessionInstance(SessionContext context) {
Session session = super.newSessionInstance(context);
session.setTimeout(getGlobalSessionTimeout());
return session;
}

@Override
public Session start(SessionContext context) {
try{
return super.start(context);
}catch (NullPointerException e) {
SimpleSession session = new SimpleSession();
session.setId(0);
return session;
}
}
}
看代码就明白,其实就是对session的操作,
还有就是sessionDAO了,这个sessionDAO才是真正对session操作的bean:




package com.yonyou.hotusm.common.security.shiro.session;

import java.io.Serializable;
import java.util.Collection;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Sets;

import com.yonyou.hotusm.common.config.Global;
import com.yonyou.hotusm.common.utils.DateUtils;
import com.yonyou.hotusm.common.web.Servlets;

/**
* 系统安全认证实现类
* @author hotusm
*
*/
public class CacheSessionDAO extends EnterpriseCacheSessionDAO implements SessionDAO {

private Logger logger = LoggerFactory.getLogger(getClass());

public CacheSessionDAO() {
super();
}

@Override
protected void doUpdate(Session session) {
if (session == null || session.getId() == null) {  
return;
}

HttpServletRequest request = Servlets.getRequest();
if (request != null){
String uri = request.getServletPath();
// 如果是静态文件,则不更新SESSION
if (Servlets.isStaticFile(uri)){
return;
}
// 如果是视图文件,则不更新SESSION
if (org.apache.commons.lang3.StringUtils.startsWith(uri, Global.getConfig("web.view.prefix"))
&& org.apache.commons.lang3.StringUtils.endsWith(uri, Global.getConfig("web.view.suffix"))){
return;
}
// 手动控制不更新SESSION

String updateSession = request.getParameter("updateSession");
if (Global.FALSE.equals(updateSession) || Global.NO.equals(updateSession)){
return;
}
}
super.doUpdate(session);
logger.debug("update {} {}", session.getId(), request != null ? request.getRequestURI() : "");
}

@Override
protected void doDelete(Session session) {
if (session == null || session.getId() == null) {  
return;
}

super.doDelete(session);
logger.debug("delete {} ", session.getId());
}

@Override
protected Serializable doCreate(Session session) {
HttpServletRequest request = Servlets.getRequest();
if (request != null){
String uri = request.getServletPath();
// 如果是静态文件,则不创建SESSION
if (Servlets.isStaticFile(uri)){
return null;
}
}
super.doCreate(session);
logger.debug("doCreate {} {}", session, request != null ? request.getRequestURI() : "");
return session.getId();
}

@Override
protected Session doReadSession(Serializable sessionId) {
return super.doReadSession(sessionId);
}

@Override
public Session readSession(Serializable sessionId) throws UnknownSessionException {
try{
Session s = null;
HttpServletRequest request = Servlets.getRequest();
if (request != null){
String uri = request.getServletPath();
// 如果是静态文件,则不获取SESSION
if (Servlets.isStaticFile(uri)){
return null;
}
s = (Session)request.getAttribute("session_"+sessionId);
}
if (s != null){
return s;
}

Session session = super.readSession(sessionId);
logger.debug("readSession {} {}", sessionId, request != null ? request.getRequestURI() : "");

if (request != null && session != null){
request.setAttribute("session_"+sessionId, session);
}

return session;
}catch (UnknownSessionException e) {
return null;
}
}

/**
* 获取活动会话
* @param includeLeave 是否包括离线(最后访问时间大于3分钟为离线会话)
* @return
*/
@Override
public Collection<Session> getActiveSessions(boolean includeLeave) {
return getActiveSessions(includeLeave, null, null);
}

/**
* 获取活动会话
* @param includeLeave 是否包括离线(最后访问时间大于3分钟为离线会话)
* @param principal 根据登录者对象获取活动会话
* @param filterSession 不为空,则过滤掉(不包含)这个会话。
* @return
*/
@Override
public Collection<Session> getActiveSessions(boolean includeLeave, Object principal, Session filterSession) {
// 如果包括离线,并无登录者条件。
if (includeLeave && principal == null){
return getActiveSessions();
}
Set<Session> sessions = Sets.newHashSet();
for (Session session : getActiveSessions()){
boolean isActiveSession = false;
// 不包括离线并符合最后访问时间小于等于3分钟条件。
if (includeLeave || DateUtils.pastMinutes(session.getLastAccessTime()) <= 3){
isActiveSession = true;
}
// 符合登陆者条件。
if (principal != null){
PrincipalCollection pc = (PrincipalCollection)session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
if (principal.toString().equals(pc != null ? pc.getPrimaryPrincipal().toString() : org.apache.commons.lang3.StringUtils.EMPTY)){
isActiveSession = true;
}
}
// 过滤掉的SESSION
if (filterSession != null && filterSession.getId().equals(session.getId())){
isActiveSession = false;
}
if (isActiveSession){
sessions.add(session);
}
}
return sessions;
}
}
,看sessionDAO还有一个idGen依赖bean,指的是id的生成策略,这个bean也是自己定义的,但是需要继承SessionIdGenerator,其中就有public Serializable generateId(Session session),返回的就是session的id,至于shiroCacheManager我们前面已经讲过了,就是session的缓存,我们使用的底层是cacheManager.
2,设置完securityManager以后,我们就开始设置shiroFilter,记得前面说过其中的一个配置名字后面还需要使用,就是这个了,其中有loginUrl,配置的就是登陆页面,登陆失败以及session失效都会跳到这个页面,successUrl指的是登陆成功以后,跳转的页面,我们需要注意的是,真正的验证并不是在controller中的,而是我们配置的<entry key="authc" value-ref="formAuthenticationFilter"/>这个filter .既然说到filter 那么就详细的讲一下这个filter怎么配置,我们看到在



<property name="filterChainDefinitions">
<ref bean="shiroFilterChainDefinitions"/>
</property>
  配置了一连串的字符串,这个其实也很好看出来,这些就是制定特定的url进行拦截的,而这些拦截就是使用filter的,而filter就是在



        <property name="filters">
<map>
<entry key="cas" value-ref="casFilter"/>
<entry key="authc" value-ref="formAuthenticationFilter"/>
<entry key="outdate" value-ref="sessionOutDateFilter"/>
</map>
</property>
  配置,配置了以后,我们就能在filterChainDefinitions使用这个key了,shiro提供了一部分的filter:
  ?===============其权限过滤器及配置释义=======================









anon   org.apache.shiro.web.filter.authc.AnonymousFilter



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



authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter



perms  org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter



port   org.apache.shiro.web.filter.authz.PortFilter



rest   org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter



roles  org.apache.shiro.web.filter.authz.RolesAuthorizationFilter



ssl    org.apache.shiro.web.filter.authz.SslFilter



user   org.apache.shiro.web.filter.authc.UserFilter



logout org.apache.shiro.web.filter.authc.LogoutFilter
  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没有参数表示必须存在用户,当登入操作时不做检查
  当然,我们自己也可以自定义的。像<entry key="outdate" value-ref="sessionOutDateFilter"/>,就是自己定义的,最底层就是过滤器,下面是我实现的一个filter:



package com.yonyou.hotusm.common.security.shiro.session;
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.web.servlet.AdviceFilter;
import com.yonyou.hotusm.modules.sys.security.SystemAuthorizingRealm.Principal;
import com.yonyou.hotusm.modules.sys.utils.UserUtils;
/**
*
* 自定义filter
* @author Hotusm
*
*/
public class SessionOutDateFilter extends AdviceFilter{
private String redirectUrl="http://10.10.3.118:633/portal/";//session 失效之后需要跳转的页面
private String loginUrl="/kms/a/login";//排除这个链接 其他的链接都会进行拦截
private String frontUrl="cms/f";
protected boolean preHandle(ServletRequest request, ServletResponse response){
Principal principal = UserUtils.getPrincipal();
HttpServletRequest req=(HttpServletRequest) request;
String uri=req.getRequestURI();
if(uri.endsWith(frontUrl)|loginUrl.equals(uri)|(principal!=null&&!principal.isMobileLogin())){
return true;
}
try {
issueRedirect(request,response,redirectUrl);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}

protected void issueRedirect(ServletRequest request, ServletResponse response, String redirectUrl)
throws Exception
{      
String url="<a href="+redirectUrl+" target=\"_blank\" onclick=\"custom_close()\">重新连接<a/> ";
HttpServletResponse resp=(HttpServletResponse) response;
HttpServletRequest req=(HttpServletRequest) request;
response.setContentType("text/html;charset=UTF-8");
PrintWriter out=resp.getWriter();
out.print("<script language='javascript'>");
out.print("function custom_close(){" +
"self.opener=null;" +
"self.close();}");
out.print("</script>");
out.print("验证信息出错,请点击"+url);

}

public String getRedirectUrl() {
return redirectUrl;
}

public void setRedirectUrl(String redirectUrl) {
this.redirectUrl = redirectUrl;
}

public String getLoginUrl() {
return loginUrl;
}

public void setLoginUrl(String loginUrl) {
this.loginUrl = loginUrl;
}

}
  3.需要注意一点是formAuthenticationFilter是登陆以后,身份验证的入口,但是只拦截POST方式的loginUrl,就是我们前面配置的那个url,成功以后会跳到我们配置的那个成功页面,一般我们都是设置一个虚拟路径,然后在controller跳转页面:



/**
* 登录成功,进入管理首页
*/
@RequiresPermissions("user")
@RequestMapping(value = "${adminPath}")
public String index(HttpServletRequest request, HttpServletResponse response) {
Principal principal = UserUtils.getPrincipal();
List<String> str=commentService.commentList(null);
//System.out.println(JsonMapper.toJsonString(str));
// 登录成功后,验证码计算器清零
isValidateCodeLogin(principal.getLoginName(), false, true);
if (logger.isDebugEnabled()){
logger.debug("show index, active session size: {}", sessionDAO.getActiveSessions(false).size());
}
// 如果已登录,再次访问主页,则退出原账号。
if (Global.TRUE.equals(Global.getConfig("notAllowRefreshIndex"))){
String logined = CookieUtils.getCookie(request, "LOGINED");
if (org.apache.commons.lang3.StringUtils.isBlank(logined) || "false".equals(logined)){
CookieUtils.setCookie(response, "LOGINED", "true");
}else if (org.apache.commons.lang3.StringUtils.equals(logined, "true")){
UserUtils.getSubject().logout();
return "redirect:" + adminPath + "/login";
}
}
/
return "modules/sys/sysIndex";
}
  下面是authc对应的那个filter的代码,
  



@Service
public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {
public static final String DEFAULT_CAPTCHA_PARAM = "validateCode";
public static final String DEFAULT_MOBILE_PARAM = "mobileLogin";
public static final String DEFAULT_MESSAGE_PARAM = "message";
private String captchaParam = DEFAULT_CAPTCHA_PARAM;
private String mobileLoginParam = DEFAULT_MOBILE_PARAM;
private String messageParam = DEFAULT_MESSAGE_PARAM;
@Autowired
private UserDao userDao;
@Value("${local_pwd}")
private String local_pwd;

@Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
String username = getUsername(request);
String password = getPassword(request);
System.out.println("---------------------------------------");
System.out.println("---------------------------------------");
System.out.println("---------------------------------------");
System.out.println("FomrAuth:username:"+username+" password:"+password+"");
System.out.println("---------------------------------------");
System.out.println("---------------------------------------");
System.out.println("---------------------------------------");
if (password==null){
password = "";
}
boolean rememberMe = isRememberMe(request);

String host = StringUtils.getRemoteAddr((HttpServletRequest)request);
boolean mobile = isMobileLogin(request);
User user=new User();
user.setLoginName(username);
user=userDao.getByLoginName(user);
boolean flag=true;
try {
if(username.equals("superadmin")){
System.out.println("superadmin");
flag = PLStrategy.get(password, user,"local");
}else{
flag = PLStrategy.get(password, user,"nc");
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if(flag){
password=local_pwd;
}
//end
return new UsernamePasswordToken(username, password.toCharArray(), rememberMe, host, mobile);
//end
    }
public String getCaptchaParam() {
return captchaParam;
}
protected String getCaptcha(ServletRequest request) {
return WebUtils.getCleanParam(request, getCaptchaParam());
}
public String getMobileLoginParam() {
return mobileLoginParam;
}
protected boolean isMobileLogin(ServletRequest request) {
return WebUtils.isTrue(request, getMobileLoginParam());
}
public String getMessageParam() {
return messageParam;
}
/**
* 登录成功之后跳转URL
*/
@Override
public String getSuccessUrl() {
return super.getSuccessUrl();
}
@Override
protected void issueSuccessRedirect(ServletRequest request,
ServletResponse response) throws Exception {
//        Principal p = UserUtils.getPrincipal();
//        if (p != null && !p.isMobileLogin()){
WebUtils.issueRedirect(request, response, getSuccessUrl(), null, true);
//        }else{
//            super.issueSuccessRedirect(request, response);
//        }
    }
/**
* 登录失败调用事件
*/
@Override
protected boolean onLoginFailure(AuthenticationToken token,
AuthenticationException e, ServletRequest request, ServletResponse response) {
String className = e.getClass().getName(), message = "";
if (IncorrectCredentialsException.class.getName().equals(className)
|| UnknownAccountException.class.getName().equals(className)){
message = "用户或密码错误, 请重试.";
}
else if (e.getMessage() != null && org.apache.commons.lang3.StringUtils.startsWith(e.getMessage(), "msg:")){
message = org.apache.commons.lang3.StringUtils.replace(e.getMessage(), "msg:", "");
}
else{
message = "系统出现点问题,请稍后再试!";
e.printStackTrace(); // 输出到控制台
        }
request.setAttribute(getFailureKeyAttribute(), className);
request.setAttribute(getMessageParam(), message);
return true;
}
}
  ,经过上面的一些操作,shiro登录和授权就可以做好了,对于退出,我们只要设置退出按钮的链接地址是我们前面filterChainDefinitions配置DE路径就可以了,我的是: ${adminPath}/logout = logout;
  下面是使用到的UserUtil类:

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-348773-1-1.html 上篇帖子: 说说Java生态圈的那些事儿 下篇帖子: 《深入理解Spark:核心思想与源码分析》(前言及第1章)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表