上一篇集成shiro的方案还有很多优化的地方,这里给出最终得结果。
1.还是从maven的pom开始
<!-- shiro start -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
</dependency>
<!-- shiro end -->
ps:这里不再需要导入freemarker+shiro标签了,自己在网上下载标签集成进去,可参考
freemarker集成shiro标签
2.spring-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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-lazy-init="true">
<description>Shiro安全配置</description>
<!--安全管理器-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!--设置自定义Realm-->
<property name="realm" ref="shiroDbRealm"/>
<!--将缓存管理器,交给安全管理器-->
<property name="cacheManager" ref="shiroSpringCacheManager"/>
<!-- 记住密码管理 -->
<property name="rememberMeManager" ref="rememberMeManager"/>
<property name="sessionManager" ref="sessionManager"/>
</bean>
<!-- 項目自定义的Realm -->
<bean id="shiroDbRealm" class="com.business.shiro.ShiroDbRealm">
<property name="cacheManager" ref="shiroSpringCacheManager" />
<!-- 自定义密码验证 ,这里不在代码中填写,直接spring注入-->
<property name="credentialsMatcher">
<bean class="com.business.shiro.CustomCredentialsMatcher" />
</property>
<!-- 启用身份验证缓存,即缓存AuthenticationInfo信息,默认false -->
<property name="authenticationCachingEnabled" value="true"/>
<!-- 缓存AuthenticationInfo信息的缓存名称 -->
<property name="authenticationCacheName" value="authenticationCache"/>
<!-- 缓存AuthorizationInfo信息的缓存名称 -->
<property name="authorizationCacheName" value="authorizationCache"/>
</bean>
<!-- 记住密码Cookie -->
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="rememberMe"/>
<property name="httpOnly" value="true"/>
<!-- 7天,采用spring el计算方便修改[细节决定成败]! -->
<property name="maxAge" value="#{7 * 24 * 60 * 60}"/>
</bean>
<!-- rememberMe管理器 -->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<property name="cookie" ref="rememberMeCookie"/>
</bean>
<bean id="formAuthenticationFilter" class="com.business.shiro.FormAuthenticationFilter">
<property name="successUrl" value="/menu/index" />
</bean>
<bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter" />
<!-- Shiro Filter -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 安全管理器 -->
<property name="securityManager" ref="securityManager"/>
<!-- 默认的登陆访问url -->
<property name="loginUrl" value="/login"/>
<!-- 登陆成功后跳转的url -->
<property name="successUrl" value="/menu/index"/>
<!-- 没有权限跳转的url -->
<property name="unauthorizedUrl" value="/login"/>
<property name="filters">
<map>
<entry key="authc" value-ref="formAuthenticationFilter"/>
<entry key="logout" value-ref="logoutFilter"/>
</map>
</property>
<property name="filterChainDefinitions">
<value>
<!--
anon 不需要认证
authc 需要认证
user 验证通过或RememberMe登录的都可以
-->
/login = authc
/logout = logout
/refund/** = anon
/favicon.ico = anon
/static/** = anon
/** = user
</value>
</property>
</bean>
<!-- 用户授权信息Cache, 采用spring-cache, 具体请查看spring-ehcache.xml、spring-redis.xml -->
<bean id="shiroSpringCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:cache/shiro-ehcache.xml" />
</bean>
<!-- 会话管理器 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!-- 设置全局会话超时时间 半小时 -->
<property name="globalSessionTimeout" value="#{30 * 60 * 1000}"/>
<property name="sessionDAO" ref="sessionDAO"/>
<property name="deleteInvalidSessions" value="true"/>
<property name="sessionValidationSchedulerEnabled" value="true" />
<property name="sessionIdCookieEnabled" value="true"/>
<property name="sessionIdCookie" ref="sessionIdCookie"/>
<!-- url上带sessionId 默认为true -->
<property name="sessionIdUrlRewritingEnabled" value="false"/>
</bean>
<!-- 会话验证调度器 -->
<bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler">
<property name="sessionValidationInterval" value="#{30 * 60 * 1000}"/>
<property name="sessionManager" ref="sessionManager"/>
</bean>
<!-- 会话DAO 用于会话的CRUD -->
<bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
<!-- Session缓存名字,默认就是shiro-activeSessionCache -->
<property name="activeSessionsCacheName" value="activeSessionCache"/>
<property name="cacheManager" ref="shiroSpringCacheManager"/>
</bean>
<!-- 会话Cookie模板 -->
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="bom_backend_ut"/>
<property name="httpOnly" value="true"/>
<property name="maxAge" value="#{7 * 24 * 60 * 60}"/>
</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>
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- AOP式方法级权限检查 ,启用了ioc容器中使用shiro注解 -->
<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>
3.ShiroDBRealm.java
package com.business.shiro;
import java.util.List;
import java.util.Set;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
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 com.business.entity.Menu;
import com.business.entity.Role;
import com.business.entity.User;
import com.business.entity.UserRole;
import com.business.service.sysService.MenuService;
import com.business.service.sysService.RoleService;
import com.business.service.sysService.UserRoleService;
import com.business.service.sysService.UserService;
import com.common.util.BizUtil;
import com.commonService.consts.Consts;
import com.google.common.collect.Sets;
/**
* @description:shiro权限认证
* @author:zhanghao
* @date:2017/5/8 14:51
*/
public class ShiroDbRealm extends AuthorizingRealm {
private static final Logger logger = LoggerFactory
.getLogger(ShiroDbRealm.class);
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
@Autowired
private UserRoleService userRoleService;
@Autowired
private MenuService menuService;
/**
* Shiro登录认证(原理:用户提交 用户名和密码 --- shiro 封装令牌 ---- realm 通过用户名将密码查询返回 ----
* shiro 自动去比较查询出密码和用户输入密码是否一致---- 进行登陆控制 )
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
throws AuthenticationException {
logger.info("Shiro开始登录认证");
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
User user = userService.getUserByLoginName(token.getUsername());
// 账号不存在
if (user == null) {
throw new UnknownAccountException();
}
// 账号未启用
if (user.getStatus() == 1) {
throw new DisabledAccountException();
}
UserRole userRole = userRoleService.getByUserId(user.getId());
Role role = roleService.getById(userRole.getRoleId());
// 读取用户的url和角色
Set<String> roles = Sets.newHashSet(role.getName());
List<Long> menuIds = BizUtil.stringToLongList(role.getMenu(),
Consts.COMMA_SEPERATOR);
List<Menu> menuList = menuService.getListByIds(menuIds);
List<String> menuStr = BizUtil.extractToList(menuList, "url");
Set<String> urls = Sets.newHashSet(menuStr);
//去空,否则会报错
urls.remove("");
urls.remove(null);
ShiroUser shiroUser = new ShiroUser(user.getId(), user.getLoginName(),
user.getUsername(), urls);
shiroUser.setRoles(roles);
// 认证缓存信息
return new SimpleAuthenticationInfo(shiroUser, user.getPassword()
.toCharArray(), getName());
}
/**
* Shiro权限认证
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setRoles(shiroUser.getRoles());
info.addStringPermissions(shiroUser.getUrlSet());
return info;
}
}
4.CustomCredentialsMatcher.java自定义密码验证,就是在spring-shiro.xml中注入的类
package com.business.shiro;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import com.business.util.MD5Util;
/**
* Description: 告诉shiro如何验证加密密码,通过SimpleCredentialsMatcher或HashedCredentialsMatcher
* @Author: zhanghao
* @Create Date: 2017-5-9
*/
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher {
@Override
public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) {
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
Object tokenCredentials = MD5Util.hmac_md5(String.valueOf(token.getPassword()));
Object accountCredentials = getCredentials(info);
//将密码加密与系统加密后的密码校验,内容一致就返回true,不一致就返回false
return equals(tokenCredentials, accountCredentials);
}
}
5.FormAuthenticationFilter.java重写认证过滤器
package com.business.shiro;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.authc.AuthenticationToken;
public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {
@Override
protected AuthenticationToken createToken(ServletRequest request,ServletResponse response) {
String username = getUsername(request);
String password = getPassword(request);
return createToken(username,username+password, request, response);
}
}
ps:这里要做的仅仅是重新创建用户token,这里我的密码是将登录名和输入的密码相加再自定义形成的最终密码
6.ShiroUser.java这里的用户信息不变
package com.business.shiro;
import java.io.Serializable;
import java.util.Set;
/**
* @description:自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息
* @author:zhanghao
* @date:2017/5/9
*/
public class ShiroUser implements Serializable {
private static final long serialVersionUID = -1373760761780840081L;
private Long id;
private final String loginName;
private String name;
private Set<String> urlSet;
private Set<String> roles;
public ShiroUser(String loginName) {
this.loginName = loginName;
}
public ShiroUser(Long id, String loginName, String name, Set<String> urlSet) {
this.id = id;
this.loginName = loginName;
this.name = name;
this.urlSet = urlSet;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<String> getUrlSet() {
return urlSet;
}
public void setUrlSet(Set<String> urlSet) {
this.urlSet = urlSet;
}
public Set<String> getRoles() {
return roles;
}
public void setRoles(Set<String> roles) {
this.roles = roles;
}
public String getLoginName() {
return loginName;
}
/**
* 本函数输出将作为默认的<shiro:principal/>输出.
*/
@Override
public String toString() {
return loginName;
}
}
7.spring-mvc-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:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util" 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.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<util:properties id="configProperties" location="classpath:init.properties" />
<context:property-placeholder properties-ref="configProperties" />
<bean id="propertyConfigurer" class="com.business.util.Configurer">
<property name="properties" ref="configProperties" />
</bean>
<!-- 启动组件扫描,只扫描@Controller组件,该组件由SpringMVC配置文件扫描 -->
<context:component-scan base-package="com.business.controller" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/plain;charset=UTF-8</value>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
<!-- 避免IE执行AJAX时,返回JSON出现下载文件 -->
<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter4">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
</list>
</property>
<property name="fastJsonConfig">
<bean class="com.alibaba.fastjson.support.config.FastJsonConfig">
<property name="serializerFeatures">
<list>
<value>DisableCircularReferenceDetect</value>
<value>WriteMapNullValue</value>
<value>WriteNullListAsEmpty</value>
<value>WriteNullStringAsEmpty</value>
<value>WriteNullNumberAsZero</value>
<value>WriteNullBooleanAsFalse</value>
<value>WriteNonStringKeyAsString</value>
<value>BrowserCompatible</value>
</list>
</property>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 状态控制器标签 -->
<mvc:status-controller path="/status" status-code="200" />
<!-- 对静态资源文件的访问 restful -->
<mvc:resources mapping="/robots.txt" location="/robots.txt" order="0"/>
<mvc:resources mapping="/static/**" location="/static/" />
<mvc:resources mapping="/favicon.ico" location="/favicon.ico"/>
<!-- 访问拦截 -->
<mvc:interceptors>
<!-- 全局拦截器 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/status"/>
<mvc:exclude-mapping path="/static/**"/>
<mvc:exclude-mapping path="/favicon.ico"/>
<bean class="com.business.interceptor.GlobalInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
<mvc:view-controller path="/" view-name="redirect:/login" />
<!-- 配置SpringMVC的视图解析器 -->
<!-- FreeMarker的配置 -->
<bean id="freeMarkerConfigurer" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/template/" />
<property name="freemarkerSettings">
<props>
<prop key="template_update_delay">0</prop>
<prop key="defaultEncoding">UTF-8</prop>
<prop key="url_escaping_charset">UTF-8</prop>
<prop key="locale">zh_CN</prop>
<prop key="boolean_format">true,false</prop>
<prop key="date_format">yyyy-MM-dd</prop>
<prop key="time_format">HH:mm:ss</prop>
<prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop>
<prop key="number_format">#.##</prop>
<prop key="whitespace_stripping">true</prop>
<prop key="classic_compatible">true</prop>
<prop key="template_exception_handler">ignore</prop>
</props>
</property>
</bean>
<!-- 配置 FreeMarker视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="viewClass" value="com.business.util.BaseFreemarkView" />
<property name="cache" value="true" />
<property name="order" value="0" />
<property name="prefix" value="" />
<property name="suffix" value=".html" />
<property name="contentType" value="text/html;charset=UTF-8" />
<property name="requestContextAttribute" value="request" />
<property name="exposeSpringMacroHelpers" value="true" />
<property name="exposeRequestAttributes" value="true" />
<property name="exposeSessionAttributes" value="true" />
</bean>
<!-- 上传拦截,如最大上传值及最小上传值 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize">
<value>104857600</value>
</property>
<property name="maxInMemorySize">
<value>4096</value>
</property>
<property name="defaultEncoding">
<value>utf-8</value>
</property>
</bean>
<!-- AOP式方法级权限检查 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true" />
</bean>
</beans>
ps:这里和shiro中都有
<!-- AOP式方法级权限检查 -->,这里使得可以直接在控制层和业务层进行注解形式的权限拦截
8.shiro-ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" dynamicConfig="false">
<diskStore path="java.io.tmpdir"/>
<!--
name:缓存名称。
maxElementsInMemory:缓存最大个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
maxElementsOnDisk:硬盘最大缓存个数。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
-->
<defaultCache name="defaultCache"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
maxElementsOnDisk="100000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" />
<cache name="authorizationCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
overflowToDisk="false"
statistics="true" />
<cache name="authenticationCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
overflowToDisk="false"
statistics="true" />
<cache name="activeSessionCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
overflowToDisk="false"
statistics="true" />
</ehcache>
9.LoginController.java登陆控制器
package com.business.controller.system;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.business.controller.BaseController;
import com.business.service.sysService.UserService;
@Controller
public class LoginController extends BaseController {
@Autowired
private UserService userService;
@RequestMapping(value = "/login",method=RequestMethod.GET)
public String login() {
Subject subject = SecurityUtils.getSubject();
// 如果已经登录,则跳转到管理首页
if(subject.isAuthenticated()){
return "redirect:/menu/index";
}
return "login";
}
/**
* 登录失败,真正登录的POST请求由Filter完成
*/
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String loginFail(HttpServletRequest request,Model model) {
Subject subject = SecurityUtils.getSubject();
// 如果已经登录,则跳转到管理首页
if(subject.isAuthenticated()){
return "redirect:/menu/index";
}
String username = WebUtils.getCleanParam(request, FormAuthenticationFilter.DEFAULT_USERNAME_PARAM);
String rememberMe = WebUtils.getCleanParam(request, FormAuthenticationFilter.DEFAULT_REMEMBER_ME_PARAM);
String exception = (String)request.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);
String message = "";
if (exception.equals(UnknownAccountException.class.getName()) || exception.equals(IncorrectCredentialsException.class.getName())){
message = "用户或密码错误!";
}else if(exception.equals(DisabledAccountException.class.getName())){
message = "账号未启用!";
}else{
message = "登录异常!";
}
model.addAttribute(FormAuthenticationFilter.DEFAULT_USERNAME_PARAM, username);
model.addAttribute(FormAuthenticationFilter.DEFAULT_REMEMBER_ME_PARAM, rememberMe);
model.addAttribute("error", message);
return "login";
}
@RequestMapping(value = "/logout")
public String logout() {
return "login";
}
}
10.BaseController.java父控制器
package com.business.controller;
import java.util.Date;
import java.util.List;
import javax.servlet.ServletException;
import org.apache.shiro.authz.AuthorizationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.propertyeditors.CustomCollectionEditor;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import com.business.result.Result;
import com.business.result.Result.ReturnResultEnum;
import com.business.shiro.ShiroUser;
import com.business.util.BackendLoginUtil;
import com.common.web.springmvc.DateTypeEditor;
public class BaseController {
protected static final Logger logger = LoggerFactory.getLogger(BaseController.class);
public ShiroUser getCurrUser() {
return BackendLoginUtil.getUser();
}
public Long getCurrUserId() {
return BackendLoginUtil.getUser().getId();
}
public Result buildSuccessMsg(String message) {
return new Result(ReturnResultEnum.SUCCESS.getCode(), message);
}
public Result buildFailMsg(String message) {
return new Result(ReturnResultEnum.FAILURE.getCode(), message);
}
protected <T> Result buildDataResult(T t) {
Result result = new Result();
result.setCode(ReturnResultEnum.SUCCESS.getCode());
result.setData(t);
return result;
}
protected <T> Result buildExceptionResult(Exception e) {
logger.info(e.getMessage());
logger.error(e.getMessage());
Result result = new Result();
result.setCode(ReturnResultEnum.FAILURE.getCode());
result.setDesc(ReturnResultEnum.FAILURE.getDesc());
return result;
}
// 如果是按标准格式,是自动可以转,如果是其他格式才String接收
@InitBinder
protected void initBinder(WebDataBinder binder) throws ServletException {
StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true);
binder.registerCustomEditor(String.class, stringTrimmerEditor);
binder.registerCustomEditor(Date.class, new DateTypeEditor());
CustomCollectionEditor collectionEditor = new CustomCollectionEditor(List.class, true);
binder.registerCustomEditor(List.class, collectionEditor);
}
//主要用来做权限判断
@ExceptionHandler(value=AuthorizationException.class)
public String exception() {
return "redirect:/logout";
}
}
ps:这里直接在父控制器中做权限判断,主要是控制层和业务层的注解形式判断,因为会在页面进行判断一次,没有权限是看不到那个按钮的,这里是防止人为破坏,直接然其跳转到登陆页面。
11.login.html登陆页面
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="renderer" content="webkit|ie-comp|ie-stand">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<LINK rel="Bookmark" href="${base}/static/favicon.ico" >
<LINK rel="Shortcut Icon" href="${base}/static/favicon.ico" />
<!--[if lt IE 9]>
<script type="text/javascript" src="lib/html5.js"></script>
<script type="text/javascript" src="lib/respond.min.js"></script>
<script type="text/javascript" src="lib/PIE_IE678.js"></script>
<![endif]-->
<link href="${base}/static/h-ui/css/H-ui.min.css" rel="stylesheet" type="text/css" />
<link href="${base}/static/h-ui.admin/css/H-ui.login.css" rel="stylesheet" type="text/css" />
<link href="${base}/static/h-ui.admin/css/style.css" rel="stylesheet" type="text/css" />
<link href="${base}/static/lib/Hui-iconfont/1.0.7/iconfont.css" rel="stylesheet" type="text/css" />
<!--[if IE 6]>
<script type="text/javascript" src="http://lib.h-ui.net/DD_belatedPNG_0.0.8a-min.js" ></script>
<script>DD_belatedPNG.fix('*');</script>
<![endif]-->
<title>*****系统</title>
</head>
<body>
<div class="header"></div>
<div class="loginWraper">
<div id="loginform" class="loginBox">
<form class="form form-horizontal" action="/login" method="post" id="form-form-horizontal">
<div class="row cl">
<div class="col-xs-3 col-offset-5">
<#if error??>
<span class="c-red">${error}</span>
</#if>
<br />
</div>
</div>
<div class="row cl">
<label class="form-label col-xs-3"><i class="Hui-iconfont"></i></label>
<div class="formControls col-xs-8 col-sm-7" style="line-height:30px;height:30px;">
<input id="username" name="username" type="text" placeholder="账户" value="${username}" class="input-text size-L" />
</div>
</div>
<div class="row cl">
<label class="form-label col-xs-3"><i class="Hui-iconfont"></i></label>
<div class="formControls col-xs-8 col-sm-7" style="line-height:30px;height:30px;">
<input id="password" name="password" type="password" placeholder="密码" value="${password}" class="input-text size-L" />
</div>
</div>
<div class="row cl">
<div class="formControls col-xs-8 col-xs-offset-3">
<label for="rememberMe"><input type="checkbox" name="rememberMe" id="rememberMe" value="${rememberMe}" <#if rememberMe=="1"> checked="checked" </#if> />使我保持登录状态</label>
</div>
</div>
<div class="row cl">
<div class="formControls col-xs-8 col-xs-offset-3">
<input type="submit" class="btn btn-success radius size-L" value=" 登 录 ">
<input type="reset" class="btn btn-default radius size-L" value=" 取 消 ">
</div>
</div>
</form>
</div>
</div>
<div class="footer">Copyright © 2015 - ${.now?string("yyyy")} *****有限公司 All Rights Reserved.</div>
<script type="text/javascript" src="${base}/static/lib/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="${base}/static/h-ui/js/H-ui.js"></script>
<script type="text/javascript" src="${base}/static/lib/jquery.validation/1.14.0/jquery.validate.min.js"></script>
<script type="text/javascript" src="${base}/static/lib/jquery.validation/1.14.0/validate-methods.js"></script>
<script type="text/javascript" src="${base}/static/lib/jquery.validation/1.14.0/messages_zh.js"></script>
<script>
$(function(){
$("#rememberMe").change(function() {
if($(this).is(':checked')){
$(this).val('1');
}else{
$(this).val('0');
}
});
$("#form-form-horizontal").validate({
rules:{
username:{
required:true,
minlength:2,
maxlength:18
},
password:{
required:true,
minlength:6,
maxlength:16
}
},
messages: {
username:{
required: "账户不能为空",
minlength: "账户长度为2到18个字符",
maxlength: "账户长度为2到18个字符"
},
password:{
required: "密码不能为空",
minlength: "密码长度为6到16个字符",
maxlength: "密码长度为6到16个字符"
}
},
onkeyup:false,
focusCleanup:false,
submitHandler:function(form){
$(form).ajaxSubmit();
var index = parent.layer.getFrameIndex(window.name);
parent.$('.btn-refresh').click();
parent.layer.close(index);
}
});
});
</script>
</body>
</html>
ps:至此全部完成,省略的部分在上两篇博客中都有。