【zheng阅读系列】shiro权限管理
一、配置文件
upms-server/springMVC-servlet.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:mvc="http://www.springframework.org/schema/mvc"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/mvc
- http://www.springframework.org/schema/mvc/spring-mvc.xsd">
- <!-- 根目录'/'对应页面 -->
- <mvc:view-controller path="/" view-name="/index.jsp"/>
- <!-- 拦截器 -->
- <mvc:interceptors>
- <!-- 获取登录信息 -->
- <mvc:interceptor>
- <mvc:mapping path="/manage/**"/>
- <bean class="com.zheng.upms.server.interceptor.UpmsInterceptor"></bean>
- </mvc:interceptor>
- </mvc:interceptors>
- <!-- Jsp视图 -->
- <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
- <property name="order" value=""/>
- <property name="prefix" value="/WEB-INF/jsp"/>
- <property name="suffix" value=""/>
- <property name="contentType" value="text/html; charset=utf-8"/>
- <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
- </bean>
- </beans>
登陆信息拦截器:
- package com.zheng.upms.server.interceptor;
- import com.zheng.common.util.PropertiesFileUtil;
- import com.zheng.upms.dao.model.UpmsUser;
- import com.zheng.upms.rpc.api.UpmsApiService;
- import com.zheng.upms.server.controller.manage.UpmsOrganizationController;
- import org.apache.shiro.SecurityUtils;
- import org.apache.shiro.subject.Subject;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.servlet.ModelAndView;
- import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- /**
- * 登录信息拦截器
- * Created by shuzheng on 2017/2/11.
- */
- public class UpmsInterceptor extends HandlerInterceptorAdapter {
- private static final Logger LOGGER = LoggerFactory.getLogger(UpmsInterceptor.class);
- private static final String ZHENG_OSS_ALIYUN_OSS_POLICY = PropertiesFileUtil.getInstance("zheng-oss-client").get("zheng.oss.aliyun.oss.policy");
- @Autowired
- UpmsApiService upmsApiService;
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- request.setAttribute("ZHENG_OSS_ALIYUN_OSS_POLICY", ZHENG_OSS_ALIYUN_OSS_POLICY);
- // 过滤ajax
- if (null != request.getHeader("X-Requested-With") && "XMLHttpRequest".equalsIgnoreCase(request.getHeader("X-Requested-With"))) {
- return true;
- }
- // 登录信息
- Subject subject = SecurityUtils.getSubject();
- String username = (String) subject.getPrincipal();
- UpmsUser upmsUser = upmsApiService.selectUpmsUserByUsername(username);
- request.setAttribute("upmsUser", upmsUser);
- return true;
- }
- @Override
- public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
- super.postHandle(request, response, handler, modelAndView);
- }
- @Override
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
- super.afterCompletion(request, response, handler, ex);
- }
- @Override
- public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- super.afterConcurrentHandlingStarted(request, response, handler);
- }
- }
zheng-upms-server:resources/profiles:dev.properties
- app.name=zheng-upms-server
- profile.env=dev
- ##### redis #####
- master.redis.ip=rdserver
- master.redis.port=
- master.redis.password=FNFl9F2O2Skb8yoKM0jhHA==
- master.redis.max_active=
- master.redis.max_idle=
- master.redis.max_wait=
- master.redis.timeout=
- ##### zheng-admin #####
- zheng.admin.version=1.0.
- ##### zheng-config #####
- #zheng.config.path=http://config.zhangshuzheng.cn:1000/${app.name}/${profile.env}
- zheng.config.path=http://127.0.0.1:1000/${app.name}/${profile.env}
- ##### zheng-upms #####
- # \u7EC8\u7AEF\u7C7B\u578B
- zheng.upms.type=server
- # \u7EC8\u7AEFsession\u540D\u79F0
- zheng.upms.session.id=zheng-upms-server-session-id
- # \u4F1A\u8BDD\u65F6\u957F,\u534A\u5C0F\u65F6\uFF08\u5355\u4F4D\u6BEB\u79D2\uFF09
- zheng.upms.session.timeout=
- # \u5355\u70B9\u767B\u5F55\u8BA4\u8BC1\u4E2D\u5FC3\u5730\u5740
- #zheng.upms.sso.server.url=http://upms.zhangshuzheng.cn:1111
- zheng.upms.sso.server.url=http://127.0.0.1:1111
- # \u767B\u5F55\u6210\u529F\u56DE\u8C03\u5730\u5740
- zheng.upms.successUrl=/manage/index
- # \u672A\u6388\u6743\u5730\u5740
- zheng.upms.unauthorizedUrl=/
- # \u8BB0\u4F4F\u5BC6\u7801\u65F6\u957F30\u5929
- zheng.upms.rememberMe.timeout=
- ##### zheng-oss #####
- #zheng.oss.aliyun.oss.policy=http://oss.zhangshuzheng.cn:7771/aliyun/oss/policy
- zheng.oss.aliyun.oss.policy=http://127.0.0.1:7771/aliyun/oss/policy
zheng-upms-client:resources/applicationContext-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:util="http://www.springframework.org/schema/util"
- xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
- <description>zheng-upms</description>
- <context:property-placeholder location="classpath*:zheng-upms-client.properties"/>
- <!-- Shiro的Web过滤器 -->
- <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
- <property name="securityManager" ref="securityManager"/>
- <property name="loginUrl" value="${zheng.upms.sso.server.url}"/>
- <property name="successUrl" value="${zheng.upms.successUrl}"/>
- <property name="unauthorizedUrl" value="${zheng.upms.unauthorizedUrl}"/>
- <property name="filters">
- <util:map>
- <entry key="authc" value-ref="upmsAuthenticationFilter"/>
- </util:map>
- </property>
- <property name="filterChainDefinitions">
- <value>
- /manage/** = upmsSessionForceLogout,authc
- /manage/index = user
- /druid/** = user
- /swagger-ui.html = user
- /resources/** = anon
- /** = anon
- </value>
- </property>
- </bean>
- <!-- 重写authc过滤器 -->
- <bean id="upmsAuthenticationFilter" class="com.zheng.upms.client.shiro.filter.UpmsAuthenticationFilter"/>
- <!-- 强制退出会话过滤器 -->
- <bean id="upmsSessionForceLogout" class="com.zheng.upms.client.shiro.filter.UpmsSessionForceLogoutFilter"/>
- <!-- 安全管理器 -->
- <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
- <property name="realms">
- <list><ref bean="upmsRealm"/></list>
- </property>
- <property name="sessionManager" ref="sessionManager"/>
- <property name="rememberMeManager" ref="rememberMeManager"/>
- </bean>
- <!-- realm实现,继承自AuthorizingRealm -->
- <bean id="upmsRealm" class="com.zheng.upms.client.shiro.realm.UpmsRealm"></bean>
- <!-- 会话管理器 -->
- <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
- <!-- 全局session超时时间 -->
- <property name="globalSessionTimeout" value="${zheng.upms.session.timeout}"/>
- <!-- sessionDAO -->
- <property name="sessionDAO" ref="sessionDAO"/>
- <property name="sessionIdCookieEnabled" value="true"/>
- <property name="sessionIdCookie" ref="sessionIdCookie"/>
- <property name="sessionValidationSchedulerEnabled" value="false"/>
- <property name="sessionListeners">
- <list><ref bean="sessionListener"/></list>
- </property>
- <property name="sessionFactory" ref="sessionFactory"/>
- </bean>
- <!-- 会话DAO,可重写,持久化session -->
- <bean id="sessionDAO" class="com.zheng.upms.client.shiro.session.UpmsSessionDao"/>
- <!-- 会话Cookie模板 -->
- <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
- <!-- 不会暴露给客户端 -->
- <property name="httpOnly" value="true"/>
- <!-- 设置Cookie的过期时间,秒为单位,默认-1表示关闭浏览器时过期Cookie -->
- <property name="maxAge" value="-1"/>
- <!-- Cookie名称 -->
- <property name="name" value="${zheng.upms.session.id}"/>
- </bean>
- <!-- 会话监听器 -->
- <bean id="sessionListener" class="com.zheng.upms.client.shiro.listener.UpmsSessionListener"/>
- <!-- session工厂 -->
- <bean id="sessionFactory" class="com.zheng.upms.client.shiro.session.UpmsSessionFactory"/>
- <!-- rememberMe管理器 -->
- <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
- <!-- rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)-->
- <property name="cipherKey" value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}"/>
- <property name="cookie" ref="rememberMeCookie"/>
- </bean>
- <!-- rememberMe缓存cookie -->
- <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
- <constructor-arg value="rememberMe"/>
- <!-- 不会暴露给客户端 -->
- <property name="httpOnly" value="true"/>
- <!-- 记住我cookie生效时间 -->
- <property name="maxAge" value="${zheng.upms.rememberMe.timeout}"/>
- </bean>
- <!-- 设置SecurityUtils,相当于调用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>
- <!-- 开启Shiro Spring AOP权限注解@RequiresPermissions的支持 -->
- <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
- <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
- <property name="securityManager" ref="securityManager"/>
- </bean>
- <!-- Shiro生命周期处理器-->
- <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
- </beans>
authc过滤器:
- package com.zheng.upms.client.shiro.filter;
- import com.alibaba.fastjson.JSONObject;
- import com.zheng.common.util.PropertiesFileUtil;
- import com.zheng.common.util.RedisUtil;
- import com.zheng.upms.client.shiro.session.UpmsSessionDao;
- import com.zheng.upms.client.util.RequestParameterUtil;
- import com.zheng.upms.common.constant.UpmsConstant;
- import org.apache.commons.lang.StringUtils;
- import org.apache.http.HttpEntity;
- import org.apache.http.HttpResponse;
- import org.apache.http.HttpStatus;
- import org.apache.http.NameValuePair;
- import org.apache.http.client.HttpClient;
- import org.apache.http.client.entity.UrlEncodedFormEntity;
- import org.apache.http.client.methods.HttpPost;
- import org.apache.http.impl.client.DefaultHttpClient;
- import org.apache.http.message.BasicNameValuePair;
- import org.apache.http.util.EntityUtils;
- import org.apache.shiro.authc.UsernamePasswordToken;
- import org.apache.shiro.session.Session;
- import org.apache.shiro.subject.Subject;
- import org.apache.shiro.web.filter.authc.AuthenticationFilter;
- import org.apache.shiro.web.util.WebUtils;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import redis.clients.jedis.Jedis;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- import java.net.URLEncoder;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * 重写authc过滤器
- * Created by shuzheng on 2017/3/11.
- */
- public class UpmsAuthenticationFilter extends AuthenticationFilter {
- private static final Logger LOGGER = LoggerFactory.getLogger(UpmsAuthenticationFilter.class);
- // 局部会话key
- private final static String ZHENG_UPMS_CLIENT_SESSION_ID = "zheng-upms-client-session-id";
- // 单点同一个code所有局部会话key
- private final static String ZHENG_UPMS_CLIENT_SESSION_IDS = "zheng-upms-client-session-ids";
- @Autowired
- UpmsSessionDao upmsSessionDao;
- @Override
- protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
- Subject subject = getSubject(request, response);
- Session session = subject.getSession();
- // 判断请求类型
- String upmsType = PropertiesFileUtil.getInstance("zheng-upms-client").get("zheng.upms.type");
- session.setAttribute(UpmsConstant.UPMS_TYPE, upmsType);
- if ("client".equals(upmsType)) {
- return validateClient(request, response);
- }
- if ("server".equals(upmsType)) {
- return subject.isAuthenticated();
- }
- return false;
- }
- @Override
- protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
- StringBuffer ssoServerUrl = new StringBuffer(PropertiesFileUtil.getInstance("zheng-upms-client").get("zheng.upms.sso.server.url"));
- // server需要登录
- String upmsType = PropertiesFileUtil.getInstance("zheng-upms-client").get("zheng.upms.type");
- if ("server".equals(upmsType)) {
- WebUtils.toHttp(response).sendRedirect(ssoServerUrl.append("/sso/login").toString());
- return false;
- }
- ssoServerUrl.append("/sso/index").append("?").append("appid").append("=").append(PropertiesFileUtil.getInstance("zheng-upms-client").get("zheng.upms.appID"));
- // 回跳地址
- HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
- StringBuffer backurl = httpServletRequest.getRequestURL();
- String queryString = httpServletRequest.getQueryString();
- if (StringUtils.isNotBlank(queryString)) {
- backurl.append("?").append(queryString);
- }
- ssoServerUrl.append("&").append("backurl").append("=").append(URLEncoder.encode(backurl.toString(), "utf-8"));
- WebUtils.toHttp(response).sendRedirect(ssoServerUrl.toString());
- return false;
- }
- /**
- * 认证中心登录成功带回code
- * @param request
- */
- private boolean validateClient(ServletRequest request, ServletResponse response) {
- Subject subject = getSubject(request, response);
- Session session = subject.getSession();
- String sessionId = session.getId().toString();
- int timeOut = (int) session.getTimeout() / ;
- // 判断局部会话是否登录
- String cacheClientSession = RedisUtil.get(ZHENG_UPMS_CLIENT_SESSION_ID + "_" + session.getId());
- if (StringUtils.isNotBlank(cacheClientSession)) {
- // 更新code有效期
- RedisUtil.set(ZHENG_UPMS_CLIENT_SESSION_ID + "_" + sessionId, cacheClientSession, timeOut);
- Jedis jedis = RedisUtil.getJedis();
- jedis.expire(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + cacheClientSession, timeOut);
- jedis.close();
- // 移除url中的code参数
- if (null != request.getParameter("code")) {
- String backUrl = RequestParameterUtil.getParameterWithOutCode(WebUtils.toHttp(request));
- HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
- try {
- httpServletResponse.sendRedirect(backUrl.toString());
- } catch (IOException e) {
- LOGGER.error("局部会话已登录,移除code参数跳转出错:", e);
- }
- } else {
- return true;
- }
- }
- // 判断是否有认证中心code
- String code = request.getParameter("upms_code");
- // 已拿到code
- if (StringUtils.isNotBlank(code)) {
- // HttpPost去校验code
- try {
- StringBuffer ssoServerUrl = new StringBuffer(PropertiesFileUtil.getInstance("zheng-upms-client").get("zheng.upms.sso.server.url"));
- HttpClient httpclient = new DefaultHttpClient();
- HttpPost httpPost = new HttpPost(ssoServerUrl.toString() + "/sso/code");
- List<NameValuePair> nameValuePairs = new ArrayList<>();
- nameValuePairs.add(new BasicNameValuePair("code", code));
- httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
- HttpResponse httpResponse = httpclient.execute(httpPost);
- if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
- HttpEntity httpEntity = httpResponse.getEntity();
- JSONObject result = JSONObject.parseObject(EntityUtils.toString(httpEntity));
- if ( == result.getIntValue("code") && result.getString("data").equals(code)) {
- // code校验正确,创建局部会话
- RedisUtil.set(ZHENG_UPMS_CLIENT_SESSION_ID + "_" + sessionId, code, timeOut);
- // 保存code对应的局部会话sessionId,方便退出操作
- RedisUtil.sadd(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + code, sessionId, timeOut);
- LOGGER.debug("当前code={},对应的注册系统个数:{}个", code, RedisUtil.getJedis().scard(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + code));
- // 移除url中的token参数
- String backUrl = RequestParameterUtil.getParameterWithOutCode(WebUtils.toHttp(request));
- // 返回请求资源
- try {
- // client无密认证
- String username = request.getParameter("upms_username");
- subject.login(new UsernamePasswordToken(username, ""));
- HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
- httpServletResponse.sendRedirect(backUrl.toString());
- return true;
- } catch (IOException e) {
- LOGGER.error("已拿到code,移除code参数跳转出错:", e);
- }
- } else {
- LOGGER.warn(result.getString("data"));
- }
- }
- } catch (IOException e) {
- LOGGER.error("验证token失败:", e);
- }
- }
- return false;
- }
- }
强制退出会话过滤器:
- package com.zheng.upms.client.shiro.filter;
- import org.apache.shiro.session.Session;
- import org.apache.shiro.web.filter.AccessControlFilter;
- import org.apache.shiro.web.util.WebUtils;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
- /**
- * 强制退出会话过滤器
- * Created by shuzheng on 2017/3/1.
- */
- public class UpmsSessionForceLogoutFilter extends AccessControlFilter {
- @Override
- protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
- Session session = getSubject(request, response).getSession(false);
- if(session == null) {
- return true;
- }
- boolean forceout = session.getAttribute("FORCE_LOGOUT") == null;
- return forceout;
- }
- @Override
- protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
- getSubject(request, response).logout();
- String loginUrl = getLoginUrl() + (getLoginUrl().contains("?") ? "&" : "?") + "forceLogout=1";
- WebUtils.issueRedirect(request, response, loginUrl);
- return false;
- }
- }
realm自定义实现:
- package com.zheng.upms.client.shiro.realm;
- import com.zheng.common.util.MD5Util;
- import com.zheng.common.util.PropertiesFileUtil;
- import com.zheng.upms.dao.model.UpmsPermission;
- import com.zheng.upms.dao.model.UpmsRole;
- import com.zheng.upms.dao.model.UpmsUser;
- import com.zheng.upms.rpc.api.UpmsApiService;
- import org.apache.commons.lang.StringUtils;
- 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.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Set;
- /**
- * 用户认证和授权
- * Created by shuzheng on 2017/1/20.
- */
- public class UpmsRealm extends AuthorizingRealm {
- private static final Logger LOGGER = LoggerFactory.getLogger(UpmsRealm.class);
- @Autowired
- private UpmsApiService upmsApiService;
- /**
- * 授权:验证权限时调用
- * @param principalCollection
- * @return
- */
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
- String username = (String) principalCollection.getPrimaryPrincipal();
- UpmsUser upmsUser = upmsApiService.selectUpmsUserByUsername(username);
- // 当前用户所有角色
- List<UpmsRole> upmsRoles = upmsApiService.selectUpmsRoleByUpmsUserId(upmsUser.getUserId());
- Set<String> roles = new HashSet<>();
- for (UpmsRole upmsRole : upmsRoles) {
- if (StringUtils.isNotBlank(upmsRole.getName())) {
- roles.add(upmsRole.getName());
- }
- }
- // 当前用户所有权限
- List<UpmsPermission> upmsPermissions = upmsApiService.selectUpmsPermissionByUpmsUserId(upmsUser.getUserId());
- Set<String> permissions = new HashSet<>();
- for (UpmsPermission upmsPermission : upmsPermissions) {
- if (StringUtils.isNotBlank(upmsPermission.getPermissionValue())) {
- permissions.add(upmsPermission.getPermissionValue());
- }
- }
- SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
- simpleAuthorizationInfo.setStringPermissions(permissions);
- simpleAuthorizationInfo.setRoles(roles);
- return simpleAuthorizationInfo;
- }
- /**
- * 认证:登录时调用
- * @param authenticationToken
- * @return
- * @throws AuthenticationException
- */
- @Override
- protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
- String username = (String) authenticationToken.getPrincipal();
- String password = new String((char[]) authenticationToken.getCredentials());
- // client无密认证
- String upmsType = PropertiesFileUtil.getInstance("zheng-upms-client").get("zheng.upms.type");
- if ("client".equals(upmsType)) {
- return new SimpleAuthenticationInfo(username, password, getName());
- }
- // 查询用户信息
- UpmsUser upmsUser = upmsApiService.selectUpmsUserByUsername(username);
- if (null == upmsUser) {
- throw new UnknownAccountException();
- }
- if (!upmsUser.getPassword().equals(MD5Util.md5(password + upmsUser.getSalt()))) {
- throw new IncorrectCredentialsException();
- }
- if (upmsUser.getLocked() == ) {
- throw new LockedAccountException();
- }
- return new SimpleAuthenticationInfo(username, password, getName());
- }
- }
会话DAO:
- package com.zheng.upms.client.shiro.session;
- import com.zheng.common.util.RedisUtil;
- import com.zheng.upms.client.util.SerializableUtil;
- import com.zheng.upms.common.constant.UpmsConstant;
- import org.apache.commons.lang.ObjectUtils;
- import org.apache.shiro.session.Session;
- import org.apache.shiro.session.mgt.ValidatingSession;
- import org.apache.shiro.session.mgt.eis.CachingSessionDAO;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import redis.clients.jedis.Jedis;
- import java.io.Serializable;
- import java.util.*;
- /**
- * 基于redis的sessionDao,缓存共享session
- * Created by shuzheng on 2017/2/23.
- */
- public class UpmsSessionDao extends CachingSessionDAO {
- private static final Logger LOGGER = LoggerFactory.getLogger(UpmsSessionDao.class);
- // 会话key
- private final static String ZHENG_UPMS_SHIRO_SESSION_ID = "zheng-upms-shiro-session-id";
- // 全局会话key
- private final static String ZHENG_UPMS_SERVER_SESSION_ID = "zheng-upms-server-session-id";
- // 全局会话列表key
- private final static String ZHENG_UPMS_SERVER_SESSION_IDS = "zheng-upms-server-session-ids";
- // code key
- private final static String ZHENG_UPMS_SERVER_CODE = "zheng-upms-server-code";
- // 局部会话key
- private final static String ZHENG_UPMS_CLIENT_SESSION_ID = "zheng-upms-client-session-id";
- // 单点同一个code所有局部会话key
- private final static String ZHENG_UPMS_CLIENT_SESSION_IDS = "zheng-upms-client-session-ids";
- @Override
- protected Serializable doCreate(Session session) {
- Serializable sessionId = generateSessionId(session);
- assignSessionId(session, sessionId);
- RedisUtil.set(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + sessionId, SerializableUtil.serialize(session), (int) session.getTimeout() / );
- LOGGER.debug("doCreate >>>>> sessionId={}", sessionId);
- return sessionId;
- }
- @Override
- protected Session doReadSession(Serializable sessionId) {
- String session = RedisUtil.get(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + sessionId);
- LOGGER.debug("doReadSession >>>>> sessionId={}", sessionId);
- return SerializableUtil.deserialize(session);
- }
- @Override
- protected void doUpdate(Session session) {
- // 如果会话过期/停止 没必要再更新了
- if(session instanceof ValidatingSession && !((ValidatingSession)session).isValid()) {
- return;
- }
- // 更新session的最后一次访问时间
- UpmsSession upmsSession = (UpmsSession) session;
- UpmsSession cacheUpmsSession = (UpmsSession) doReadSession(session.getId());
- if (null != cacheUpmsSession) {
- upmsSession.setStatus(cacheUpmsSession.getStatus());
- upmsSession.setAttribute("FORCE_LOGOUT", cacheUpmsSession.getAttribute("FORCE_LOGOUT"));
- }
- RedisUtil.set(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + session.getId(), SerializableUtil.serialize(session), (int) session.getTimeout() / );
- // 更新ZHENG_UPMS_SERVER_SESSION_ID、ZHENG_UPMS_SERVER_CODE过期时间 TODO
- LOGGER.debug("doUpdate >>>>> sessionId={}", session.getId());
- }
- @Override
- protected void doDelete(Session session) {
- String sessionId = session.getId().toString();
- String upmsType = ObjectUtils.toString(session.getAttribute(UpmsConstant.UPMS_TYPE));
- if ("client".equals(upmsType)) {
- // 删除局部会话和同一code注册的局部会话
- String code = RedisUtil.get(ZHENG_UPMS_CLIENT_SESSION_ID + "_" + sessionId);
- Jedis jedis = RedisUtil.getJedis();
- jedis.del(ZHENG_UPMS_CLIENT_SESSION_ID + "_" + sessionId);
- jedis.srem(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + code, sessionId);
- jedis.close();
- }
- if ("server".equals(upmsType)) {
- // 当前全局会话code
- String code = RedisUtil.get(ZHENG_UPMS_SERVER_SESSION_ID + "_" + sessionId);
- // 清除全局会话
- RedisUtil.remove(ZHENG_UPMS_SERVER_SESSION_ID + "_" + sessionId);
- // 清除code校验值
- RedisUtil.remove(ZHENG_UPMS_SERVER_CODE + "_" + code);
- // 清除所有局部会话
- Jedis jedis = RedisUtil.getJedis();
- Set<String> clientSessionIds = jedis.smembers(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + code);
- for (String clientSessionId : clientSessionIds) {
- jedis.del(ZHENG_UPMS_CLIENT_SESSION_ID + "_" + clientSessionId);
- jedis.srem(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + code, clientSessionId);
- }
- LOGGER.debug("当前code={},对应的注册系统个数:{}个", code, jedis.scard(ZHENG_UPMS_CLIENT_SESSION_IDS + "_" + code));
- jedis.close();
- // 维护会话id列表,提供会话分页管理
- RedisUtil.lrem(ZHENG_UPMS_SERVER_SESSION_IDS, , sessionId);
- }
- // 删除session
- RedisUtil.remove(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + sessionId);
- LOGGER.debug("doUpdate >>>>> sessionId={}", sessionId);
- }
- /**
- * 获取会话列表
- * @param offset
- * @param limit
- * @return
- */
- public Map getActiveSessions(int offset, int limit) {
- LOGGER.info("进入getActiveSession获取当前活动的session数据,服务端分页需要返回total 和rows");
- Map sessions = new HashMap();
- Jedis jedis = RedisUtil.getJedis();
- // 获取在线会话总数
- long total = jedis.llen(ZHENG_UPMS_SERVER_SESSION_IDS);
- // 获取当前页会话详情
- List<String> ids = jedis.lrange(ZHENG_UPMS_SERVER_SESSION_IDS, offset, (offset + limit - ));
- List<Session> rows = new ArrayList<>();
- for (String id : ids) {
- String session = RedisUtil.get(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + id);
- // 过滤redis过期session
- if (null == session) {
- RedisUtil.lrem(ZHENG_UPMS_SERVER_SESSION_IDS, , id);
- total = total - ;
- continue;
- }
- rows.add(SerializableUtil.deserialize(session));
- }
- jedis.close();
- sessions.put("total", total);
- sessions.put("rows", rows);
- return sessions;
- }
- /**
- * 强制退出
- * @param ids
- * @return
- */
- public int forceout(String ids) {
- String[] sessionIds = ids.split(",");
- for (String sessionId : sessionIds) {
- // 会话增加强制退出属性标识,当此会话访问系统时,判断有该标识,则退出登录
- String session = RedisUtil.get(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + sessionId);
- UpmsSession upmsSession = (UpmsSession) SerializableUtil.deserialize(session);
- upmsSession.setStatus(UpmsSession.OnlineStatus.force_logout);
- upmsSession.setAttribute("FORCE_LOGOUT", "FORCE_LOGOUT");
- RedisUtil.set(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + sessionId, SerializableUtil.serialize(upmsSession), (int) upmsSession.getTimeout() / );
- }
- return sessionIds.length;
- }
- /**
- * 更改在线状态
- *
- * @param sessionId
- * @param onlineStatus
- */
- public void updateStatus(Serializable sessionId, UpmsSession.OnlineStatus onlineStatus) {
- UpmsSession session = (UpmsSession) doReadSession(sessionId);
- if (null == session) {
- return;
- }
- session.setStatus(onlineStatus);
- RedisUtil.set(ZHENG_UPMS_SHIRO_SESSION_ID + "_" + session.getId(), SerializableUtil.serialize(session), (int) session.getTimeout() / );
- }
- }
会话监听器:
- package com.zheng.upms.client.shiro.listener;
- import org.apache.shiro.session.Session;
- import org.apache.shiro.session.SessionListener;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- /**
- * Created by shuzheng on 2017/2/12.
- */
- public class UpmsSessionListener implements SessionListener {
- private static final Logger LOGGER = LoggerFactory.getLogger(UpmsSessionListener.class);
- @Override
- public void onStart(Session session) {
- LOGGER.debug("会话创建:" + session.getId());
- }
- @Override
- public void onStop(Session session) {
- LOGGER.debug("会话停止:" + session.getId());
- }
- @Override
- public void onExpiration(Session session) {
- LOGGER.debug("会话过期:" + session.getId());
- }
- }
会话工厂:
- package com.zheng.upms.client.shiro.session;
- import org.apache.shiro.session.Session;
- import org.apache.shiro.session.mgt.SessionContext;
- import org.apache.shiro.session.mgt.SessionFactory;
- import org.apache.shiro.web.session.mgt.WebSessionContext;
- import javax.servlet.http.HttpServletRequest;
- /**
- * session工厂
- * Created by shuzheng on 2017/2/27.
- */
- public class UpmsSessionFactory implements SessionFactory {
- @Override
- public Session createSession(SessionContext sessionContext) {
- UpmsSession session = new UpmsSession();
- if (null != sessionContext && sessionContext instanceof WebSessionContext) {
- WebSessionContext webSessionContext = (WebSessionContext) sessionContext;
- HttpServletRequest request = (HttpServletRequest) webSessionContext.getServletRequest();
- if (null != request) {
- session.setHost(request.getRemoteAddr());
- session.setUserAgent(request.getHeader("User-Agent"));
- }
- }
- return session;
- }
- }
二、日志记录切面
springMVC-servlet.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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
通过配置织入@Aspectj切面
- <aop:aspectj-autoproxy proxy-target-class="true"/>
- <!-- 日志记录AOP实现 -->
- <bean class="com.zheng.upms.client.interceptor.LogAspect"/>
- <!-- 日志记录AOP实现 -->
- <bean class="com.zheng.common.aspect.RpcLogAspect"/>
- </beans>
LogAspect实现:
- package com.zheng.upms.client.interceptor;
- import com.alibaba.fastjson.JSON;
- import com.zheng.common.util.RequestUtil;
- import com.zheng.upms.dao.model.UpmsLog;
- import com.zheng.upms.rpc.api.UpmsApiService;
- import io.swagger.annotations.ApiOperation;
- import org.apache.commons.lang.ObjectUtils;
- import org.apache.shiro.authz.annotation.RequiresPermissions;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.Signature;
- import org.aspectj.lang.annotation.After;
- import org.aspectj.lang.annotation.Around;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Before;
- import org.aspectj.lang.reflect.MethodSignature;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.context.request.RequestAttributes;
- import org.springframework.web.context.request.RequestContextHolder;
- import org.springframework.web.context.request.ServletRequestAttributes;
- import javax.servlet.http.HttpServletRequest;
- import java.lang.reflect.Method;
- /**
- * 日志记录AOP实现
- * Created by ZhangShuzheng on 2017/3/14.
- */
- @Aspect
- public class LogAspect {
- private static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class);
- // 开始时间
- private long startTime = 0L;
- // 结束时间
- private long endTime = 0L;
- @Autowired
- UpmsApiService upmsApiService;
- @Before("execution(* *..controller..*.*(..))")
- public void doBeforeInServiceLayer(JoinPoint joinPoint) {
- LOGGER.debug("doBeforeInServiceLayer");
- startTime = System.currentTimeMillis();
- }
- @After("execution(* *..controller..*.*(..))")
- public void doAfterInServiceLayer(JoinPoint joinPoint) {
- LOGGER.debug("doAfterInServiceLayer");
- }
- @Around("execution(* *..controller..*.*(..))")
- public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
- // 获取request
- RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
- ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
- HttpServletRequest request = servletRequestAttributes.getRequest();
- UpmsLog upmsLog = new UpmsLog();
- // 从注解中获取操作名称、获取响应结果
- Object result = pjp.proceed();
- Signature signature = pjp.getSignature();
- MethodSignature methodSignature = (MethodSignature) signature;
- Method method = methodSignature.getMethod();
- if (method.isAnnotationPresent(ApiOperation.class)) {
- ApiOperation log = method.getAnnotation(ApiOperation.class);
- upmsLog.setDescription(log.value());
- }
- if (method.isAnnotationPresent(RequiresPermissions.class)) {
- RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class);
- String[] permissions = requiresPermissions.value();
- if (permissions.length > ) {
- upmsLog.setPermissions(permissions[]);
- }
- }
- endTime = System.currentTimeMillis();
- LOGGER.debug("doAround>>>result={},耗时:{}", result, endTime - startTime);
- upmsLog.setBasePath(RequestUtil.getBasePath(request));
- upmsLog.setIp(RequestUtil.getIpAddr(request));
- upmsLog.setMethod(request.getMethod());
- if ("GET".equalsIgnoreCase(request.getMethod())) {
- upmsLog.setParameter(request.getQueryString());
- } else {
- upmsLog.setParameter(ObjectUtils.toString(request.getParameterMap()));
- }
- upmsLog.setResult(JSON.toJSONString(result));
- upmsLog.setSpendTime((int) (endTime - startTime));
- upmsLog.setStartTime(startTime);
- upmsLog.setUri(request.getRequestURI());
- upmsLog.setUrl(ObjectUtils.toString(request.getRequestURL()));
- upmsLog.setUserAgent(request.getHeader("User-Agent"));
- upmsLog.setUsername(ObjectUtils.toString(request.getUserPrincipal()));
- upmsApiService.insertUpmsLogSelective(upmsLog);
- return result;
- }
- }
rpc消费日志记录:
- package com.zheng.common.aspect;
- import com.alibaba.dubbo.rpc.RpcContext;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.After;
- import org.aspectj.lang.annotation.Around;
- import org.aspectj.lang.annotation.Before;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- /**
- * rpc提供者和消费者日志打印
- * Created by ZhangShuzheng on 2017/4/19.
- */
- public class RpcLogAspect {
- private static final Logger LOGGER = LoggerFactory.getLogger(RpcLogAspect.class);
- // 开始时间
- private long startTime = 0L;
- // 结束时间
- private long endTime = 0L;
- @Before("execution(* *..rpc..*.*(..))")
- public void doBeforeInServiceLayer(JoinPoint joinPoint) {
- LOGGER.debug("doBeforeInServiceLayer");
- startTime = System.currentTimeMillis();
- }
- @After("execution(* *..rpc..*.*(..))")
- public void doAfterInServiceLayer(JoinPoint joinPoint) {
- LOGGER.debug("doAfterInServiceLayer");
- }
- @Around("execution(* *..rpc..*.*(..))")
- public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
- Object result = pjp.proceed();
- // 是否是消费端
- boolean consumerSide = RpcContext.getContext().isConsumerSide();
- // 获取最后一次提供方或调用方IP
- String ip = RpcContext.getContext().getRemoteHost();
- // 服务url
- String rpcUrl = RpcContext.getContext().getUrl().getParameter("application");
- LOGGER.info("consumerSide={}, ip={}, url={}", consumerSide, ip, rpcUrl);
- return result;
- }
- }
二、session管理页面
- <%@ page contentType="text/html; charset=utf-8"%>
- <%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%>
- <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
- <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
- <%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
- <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
- <%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
- <c:set var="basePath" value="${pageContext.request.contextPath}"/>
- <!DOCTYPE HTML>
- <html lang="zh-cn">
- <head>
- <meta charset="utf-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title>会话管理</title>
- <jsp:include page="/resources/inc/head.jsp" flush="true"/>
- </head>
- <body>
- <div id="main">
- <div id="toolbar">
- <shiro:hasPermission name="upms:session:forceout"><a class="waves-effect waves-button" href="javascript:;" onclick="forceoutAction()"><i class="zmdi zmdi-run"></i> 强制退出</a></shiro:hasPermission>
- </div>
- <table id="table"></table>
- </div>
- <jsp:include page="/resources/inc/footer.jsp" flush="true"/>
- <script>
- var $table = $('#table');
- $(function() {
- // bootstrap table初始化
- $table.bootstrapTable({
- //向服务器请求的url。
- url: '${basePath}/manage/session/list',
- //表格的高度
- height: getHeight(),
- //默认false,当设为true,则每行表格的背景会显示灰白相间
- striped: true,
- //默认false不显示表格右上方搜索框 ,可设为true,在搜索框内只要输入内容即开始搜索
- search: false,
- //默认为false隐藏刷新按钮,设为true显示
- showRefresh: true,
- //默认为false隐藏某列下拉菜单,设为true显示
- showColumns: true,
- //每列的下拉菜单最小数
- minimumCountColumns: ,
- //默认false不响应,设为true则当点击此行的某处时,会自动选中此行的checkbox(复选框)或radiobox(单选按钮)
- clickToSelect: true,
- //默认false,设为true显示detail view(细节视图)
- detailView: true,
- //前提:detailView设为true,启用了显示detail view。
- // 用于格式化细节视图
- // 返回一个字符串,通过第三个参数element直接添加到细节视图的cell(某一格)中,其中,element为目标cell的jQuery element
- detailFormatter: 'detailFormatter',
- //默认为false,表格的底部工具栏不会显示分页条(pagination toolbar),可以设为true来显示
- pagination: true,
- //默认true,分页条无限循环
- paginationLoop: false,
- //设置在哪进行分页,默认”client”,可选”server”,如果设置 “server”,则必须设置url或者重写ajax方法
- sidePagination: 'server',
- //设置为 false 将在点击分页按钮时,自动记住排序项。仅在 sidePagination设置为 server时生效。
- silentSort: false,
- //设置为 true 是程序自动判断显示分页信息和 card 视图。
- smartDisplay: false,
- //转义HTML字符串,替换 &, <, >, ", \`, 和 ' 字符。
- escape: true,
- //设置为 true时,按回车触发搜索方法,否则自动触发搜索方法。
- searchOnEnterKey: true,
- //指定主键列。
- idField: 'id',
- //设置为 true 在点击分页按钮或搜索按钮时,将记住checkbox的选择项。\
- maintainSelected: true,
- //一个jQuery 选择器,指明自定义的 toolbar。例如:#toolbar, .toolbar.
- toolbar: '#toolbar',
- //列配置项,详情请查看 列参数 表格.
- columns: [
- {field: 'ck', checkbox: true},
- {field: 'id', title: '编号', sortable: true, align: 'center'},
- {field: 'startTimestamp', title: '创建时间', sortable: true, align: 'center'},
- {field: 'lastAccessTime', title: '最后访问时间'},
- {field: 'expired', title: '是否过期', align: 'center'},
- {field: 'host', title: '访问者IP', align: 'center'},
- {field: 'userAgent', title: '用户标识', align: 'center'},
- {field: 'status', title: '状态', align: 'center', formatter: 'statusFormatter'}
- ]
- });
- });
- // 格式化状态
- function statusFormatter(value, row, index) {
- if (value == 'on_line') {
- return '<span class="label label-success">在线</span>';
- }
- if (value == 'off_line') {
- return '<span class="label label-default">离线</span>';
- }
- if (value == 'force_logout') {
- return '<span class="label label-danger">踢离</span>';
- }
- }
- // 强制退出
- var forceoutDialog;
- function forceoutAction() {
- //getSelections方法返回所选的行,当没有选择任何行的时候返回一个空数组。
- var rows = $table.bootstrapTable('getSelections');
- if (rows.length == ) {
- $.confirm({
- title: false,
- content: '请至少选择一条记录!',
- autoClose: 'cancel|3000',
- backgroundDismiss: true,
- buttons: {
- cancel: {
- text: '取消',
- btnClass: 'waves-effect waves-button'
- }
- }
- });
- } else {
- forceoutDialog = $.confirm({
- type: 'red',
- animationSpeed: ,
- title: false,
- content: '确认强制退出该会话吗?',
- buttons: {
- confirm: {
- text: '确认',
- btnClass: 'waves-effect waves-button',
- action: function () {
- var ids = new Array();
- for (var i in rows) {
- ids.push(rows[i].id);
- }
- $.ajax({
- type: 'get',
- url: '${basePath}/manage/session/forceout/' + ids.join(","),
- success: function(result) {
- if (result.code != ) {
- if (result.data instanceof Array) {
- $.each(result.data, function(index, value) {
- $.confirm({
- theme: 'dark',
- animation: 'rotateX',
- closeAnimation: 'rotateX',
- title: false,
- content: value.errorMsg,
- buttons: {
- confirm: {
- text: '确认',
- btnClass: 'waves-effect waves-button waves-light'
- }
- }
- });
- });
- } else {
- $.confirm({
- theme: 'dark',
- animation: 'rotateX',
- closeAnimation: 'rotateX',
- title: false,
- content: result.data.errorMsg,
- buttons: {
- confirm: {
- text: '确认',
- btnClass: 'waves-effect waves-button waves-light'
- }
- }
- });
- }
- } else {
- forceoutDialog.close();
- $table.bootstrapTable('refresh');
- }
- },
- error: function(XMLHttpRequest, textStatus, errorThrown) {
- $.confirm({
- theme: 'dark',
- animation: 'rotateX',
- closeAnimation: 'rotateX',
- title: false,
- content: textStatus,
- buttons: {
- confirm: {
- text: '确认',
- btnClass: 'waves-effect waves-button waves-light'
- }
- }
- });
- }
- });
- }
- },
- cancel: {
- text: '取消',
- btnClass: 'waves-effect waves-button'
- }
- }
- });
- }
- }
- </script>
- </body>
- </html>
common.js
- $(function() {
- // Waves初始化
- Waves.displayEffect();
- // 数据表格动态高度
- $(window).resize(function () {
- $('#table').bootstrapTable('resetView', {
- height: getHeight()
- });
- });
- // 设置input特效
- $(document).on('focus', 'input[type="text"]', function() {
- $(this).parent().find('label').addClass('active');
- }).on('blur', 'input[type="text"]', function() {
- if ($(this).val() == '') {
- $(this).parent().find('label').removeClass('active');
- }
- });
- // select2初始化
- $('select').select2();
- });
- // 动态高度
- function getHeight() {
- return $(window).height() - ;
- }
- // 数据表格展开内容
- function detailFormatter(index, row) {
- var html = [];
- $.each(row, function (key, value) {
- html.push('<p><b>' + key + ':</b> ' + value + '</p>');
- });
- return html.join('');
- }
- // 初始化input特效
- function initMaterialInput() {
- $('form input[type="text"]').each(function () {
- if ($(this).val() != '') {
- $(this).parent().find('label').addClass('active');
- }
- });
- }
【zheng阅读系列】shiro权限管理的更多相关文章
- (39.4) Spring Boot Shiro权限管理【从零开始学Spring Boot】
在读此文章之前您还可能需要先了解: (39.1) Spring Boot Shiro权限管理[从零开始学Spring Boot] http://412887952-qq-com.iteye.com/b ...
- (39.3) Spring Boot Shiro权限管理【从零开始学Spring Boot】
在学习此小节之前您可能还需要学习: (39.1) Spring Boot Shiro权限管理[从零开始学Spring Boot] http://412887952-qq-com.iteye.com/b ...
- (39.2). Spring Boot Shiro权限管理【从零开始学Spring Boot】
(本节提供源代码,在最下面可以下载) (4). 集成Shiro 进行用户授权 在看此小节前,您可能需要先看: http://412887952-qq-com.iteye.com/blog/229973 ...
- (39.1) Spring Boot Shiro权限管理【从零开始学Spring Boot】
(本节提供源代码,在最下面可以下载)距上一个章节过了二个星期了,最近时间也是比较紧,一直没有时间可以写博客,今天难得有点时间,就说说Spring Boot如何集成Shiro吧.这个章节会比较复杂,牵涉 ...
- Spring Boot Shiro 权限管理
Spring Boot Shiro 权限管理 标签: springshiro 2016-01-14 23:44 94587人阅读 评论(60) 收藏 举报 .embody{ padding:10px ...
- Spring Boot Shiro 权限管理 【转】
http://blog.csdn.net/catoop/article/details/50520958 主要用于备忘 本来是打算接着写关于数据库方面,集成MyBatis的,刚好赶上朋友问到Shiro ...
- shiro权限管理的框架-入门
shiro权限管理的框架 1.权限管理的概念 基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能 ...
- (补漏)Springboot2.0 集成shiro权限管理
原文Springboot2.0 集成shiro权限管理 一.关于停止使用外键. 原本集成shiro建立用户.角色.权限表的时候使用了外键,系统自动创建其中两个关联表,用@JoinTable.看起来省事 ...
- SpringMVC+Shiro权限管理(转载)
源码 http://pan.baidu.com/s/1pJzG4t1 SpringMVC+Shiro权限管理 博文目录 权限的简单描述 实例表结构及内容及POJO Shiro-pom.xml Shir ...
随机推荐
- Android四大组件应用系列——实现电话拦截和电话录音
一.问题描述 使用BordercastReceiver和Service组件实现下述功能: 1.当手机处于来电状态,启动监听服务,对来电进行监听录音. 2.设置电话黑名单,当来电是黑名单电话,则直接挂断 ...
- [Mockito] Spring Unit Testing with Mockito
It is recommened to write unit testing with Mockito in Spring framework, because it is much faster w ...
- Redis深入之对象
Redis对象系统 前面介绍了Redis用到的全部主要数据结构,如简单动态字符串(SDS).双端链表.字典.压缩列表.整数集合等 Redis并没有直接使用这些数据结构来实现键值对数据库.而是基于这些数 ...
- akka actors默认邮箱介绍
1. UnboundedMailbox is the default unbounded MailboxType used by Akka Actors ”无界邮箱“ 是akka actors默认使用 ...
- volitile关键字
1.volatile关键字的两层语义 一旦一个共享变量(类的成员变量.类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修 ...
- Authentication 方案优化探索(JWT, Session, Refresh Token, etc.)
转载自:http://www.jianshu.com/p/5ac8a0e1e5a8
- netstat使用--10个常用的命令
1.列出所有的端口 netstat -a 列出TCP协议的端口 netstat -at UDP协议的端口 netstat -au 2.列出处于监听状态的socket netstat - ...
- AaronYang WCF教程目录
============================原创,讲究实践===================== 1. 那天有个小孩教我WCF[一][1/3] 基本搭建 阅读 ...
- 译:3.消费一个RESTful Web Service
这节课我们根据官网教程学习如何去消费(调用)一个 RESTful Web Service . 原文链接 https://spring.io/guides/gs/consuming-rest/ 本指南将 ...
- ThreadPoolExecutor 线程池浅析
作为Executor框架中最核心的类,ThreadPoolExecutor代表着鼎鼎大名的线程池,它给了我们足够的理由来弄清楚它. 下面我们就通过源码来一步一步弄清楚它. 内部状态 线程有五种状态:新 ...