學習筆記(1)鏈接:http://blog.csdn.net/qq_15370821/article/details/52798276
一個簡單的權限管理系統,實現用戶、權限、資源的增刪改查,這三者之間相互的授權和取消授權,如:一個用戶可以擁有多個權限並且一個權限可以擁有多個資源。
系統基於shiro、springmvc、spring、mybatis,使用MySQL數據庫。
項目地址:https://git.oschina.net/beyondzl/spring-shiro
(前端視圖由於時間原因沒有全部完成,後端功能測試可行)
Web層
springmvc.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<mvc:annotation-driven >
<mvc:message-converters register-defaults="false">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="utf-8"></constructor-arg>
</bean>
<ref bean="jacksonMessageConverter"></ref>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
<property name="objectMapper" ref="jacksonObjectMapper"></property>
</bean>
<context:component-scan base-package="com.spring.shiro.main.java.controller"></context:component-scan>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
<!-- 有多個視圖解析器時的優先級 -->
<property name="order" value="10"></property>
</bean>
</beans>
基礎Controller類:
public abstract class BaseController {
@Autowired
private UserService userService ;
/**
* 日期格式轉換
* @param binder
*/
@InitBinder
public void initBinder(ServletRequestDataBinder binder) {
binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
/**
* 防止XSS攻擊
*/
binder.registerCustomEditor(String.class, new StringEscapeEditor(true,false));
}
/**
* 獲取當前登錄用戶
* @return
*/
public User getCurrentUser() {
ShiroUser user = (ShiroUser) SecurityUtils.getSubject().getPrincipal();
User currentUser = userService.findUserById(user.id);
return currentUser;
}
/*
* 獲取當前用戶id
*/
public Long getUserUd() {
return this.getCurrentUser().getId();
}
/**
* 獲取當前用戶名
* @return
*/
public String getStaffName() {
return this.getCurrentUser().getName();
}
/**
* ajax失敗
* @param msg 失敗的消息
* @return
*/
public Object renderError(String msg) {
Result result = new Result();
result.setMsg(msg);
return result;
}
/**
* ajax成功
* @return
*/
public Object renderSuccess() {
Result result = new Result();
result.setSuccess(true);
return result;
}
/**
* ajax成功消息
* @param msg
* @return
*/
public Object renderSuccess(String msg) {
Result result = new Result();
result.setSuccess(true);
result.setMsg(msg);
return result;
}
/**
* ajax成功時的對象
* @param obj
* @return
*/
public Object renderSuccess(Object obj) {
Result result = new Result();
result.setSuccess(true);
result.setObj(obj);
return result;
}
}
handler繼承基本Controller類:
@Controller
@RequestMapping("/user")
public class UserController extends BaseController {
@Autowired
private UserService userService;
/**
* 用戶管理頁
* @return
*/
@RequestMapping(value="/manager",method=RequestMethod.GET)
public String manager() {
return "/admin/user";
}
/**
* 用戶管理列表
* @param userVo
* @param page
* @param rows
* @param sort
* @return
*/
@RequestMapping(value="/dataGrid", method=RequestMethod.POST)
@ResponseBody
public Object dataGrid(UserVo userVo, Integer page, Integer rows, String sort) {
PageInfo pageInfo = new PageInfo(page, rows);
Map<String, Object> condition = new HashMap<String, Object>();
if(StringUtils.hasText(userVo.getName())) {
condition.put("name", userVo.getName());
}
if(userVo.getOrganizationId() != null) {
condition.put("organizationId", userVo.getOrganizationId());
}
if(userVo.getCreatedateStart() != null) {
condition.put("startTime", userVo.getCreatedateStart());
}
if(userVo.getCreatedateEnd() != null) {
condition.put("endTime", userVo.getCreatedateEnd());
}
pageInfo.setCondition(condition);
userService.findUserGrid(pageInfo);
return pageInfo;
}
/**
* 添加用戶頁
* @return
*/
@RequestMapping(value="addPage", method=RequestMethod.GET)
public String addPage() {
return "/admin/userAdd";
}
/**
* 添加用戶
* @param userVo
* @return
*/
@RequestMapping(value="/add",method=RequestMethod.POST)
@ResponseBody
public Object add(UserVo userVo) {
User user = userService.findUserByLoginName(userVo.getLoginName());
if(user != null) {
return renderError("用戶名已存在!");
}
try {
userVo.setPassword(DigestUtils.md5Hex(userVo.getPassword().getBytes("UTF-8")));
} catch (UnsupportedEncodingException e) {
return renderSuccess("添加失敗!");
}
userService.addUser(userVo);
return renderSuccess("添加成功!");
}
/**
* 編輯用戶頁
* @param id
* @param model
* @return
*/
@RequestMapping("/editPage")
public String editPage(Long id,Model model) {
UserVo userVo = userService.findUserVoById(id);
List<Role> rolesList = userVo.getRolesList();
List<Long> idsList = new ArrayList<Long>();
for(Role role : rolesList) {
idsList.add(role.getId());
}
model.addAttribute("roleIds", rolesList);
model.addAttribute("user", userVo);
return "/admin/userEdit";
}
@RequestMapping("/edit")
@ResponseBody
public Object edit(UserVo userVo) {
User user = userService.findUserByLoginName(userVo.getLoginName());
if(user != null && user.getId() != userVo.getId()) {
return renderError("用戶名已存在!");
}
try {
userVo.setPassword(DigestUtils.md5Hex(userVo.getPassword().getBytes("UTF-8")));
} catch (UnsupportedEncodingException e) {
return renderError("添加失敗!");
}
userService.addUser(userVo);
return renderSuccess("添加成功!");
}
/**
* 修改密碼頁
* @return
*/
@RequestMapping(value="/editPwdPage", method=RequestMethod.GET)
public String editPwdPage() {
return "/admin/userEditPwd";
}
@RequestMapping("/editUserPwd")
@ResponseBody
public Object editUserPwd(String oldPwd, String pwd) {
try {
if(!getCurrentUser().getPassword().equals(DigestUtils.md5Hex(oldPwd.getBytes("UTF-8")))) {
return renderError("舊密碼錯誤!");
}
} catch (UnsupportedEncodingException e) {
return renderError("修改失敗!");
}
userService.updateUserPwdById(getUserUd(), pwd);
return renderSuccess("修改成功!");
}
@RequestMapping("/delete")
@ResponseBody
public Object delete(Long id) {
userService.deleteUserById(id);
return renderSuccess("刪除成功!");
}
}
spirng整合shiro
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">
<description>shiro安全配置</description>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" >
<!-- 設置自定義realm -->
<property name="realm" ref="shiroDbRealm"></property>
<!-- 將緩存管理器交給安全管理器 -->
<property name="cacheManager" ref="shiroEhcacheManager"></property>
</bean>
<!-- 自定義realm -->
<bean id="shiroDbRealm" class="com.spring.shiro.main.common.shiro.ShiroDbRealm"></bean>
<!-- shiro Filter -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 安全管理器 -->
<property name="securityManager" ref="securityManager"></property>
<!-- 默認的登陸訪問url -->
<property name="loginUrl" value="/login"></property>
<!-- 登陸成功後跳轉的url -->
<property name="successUrl" value="/index"></property>
<!-- 沒有權限跳轉的url -->
<property name="unauthorizedUrl" value="/unauth"></property>
<property name="filterChainDefinitions">
<value>
/commons/** = anon <!-- 不需要認證 -->
/static/** = anon
/login = anon
/** = authc <!-- 需要認證 -->
</value>
</property>
</bean>
<!-- 用戶授權信息cache 才用Ehcache -->
<bean id="ShiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml"></property>
</bean>
<!-- 在方法中注入securityManager進行代理控制 -->
<bean class="org.springframework.beans.factory.config.MethodInvokingBean">
<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"></property>
<property name="arguments" ref="securityManager"></property>
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>
<!-- AOP方法級權限檢查 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"></bean>
<!-- 啓用shiro授權註解攔截方式 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"></property>
</bean>
</beans>
ehcache配置
ehcache-shiro.xml:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" name="shiroCache">
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
overToLiveSeconds="120"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
</ehcache>
shiro權限認證類:
public class ShiroDbRealm extends AuthorizingRealm {
private static Logger LOGGER = LogManager.getLogger(ShiroDbRealm.class);
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
/**
* 權限認證
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
ShiroUser shiroUser = (ShiroUser)principal.getPrimaryPrincipal();
List<Long> roleList = shiroUser.roleList;
Set<String> urlSet = new HashSet<String>();
for(Long roleId : roleList) {
List<Map<Long, String>> roleResourceList = roleService.findRoleResourceListByRoleId(roleId);
if(roleResourceList != null) {
for(Map<Long,String> map : roleResourceList) {
if(StringUtils.hasText(map.get("url"))) {
urlSet.add(map.get("url"));
}
}
}
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(urlSet);
return info;
}
/**
* shiro登陸認證 用戶提交用戶名密碼,shiro封裝令牌,realm通過用戶名密碼查詢返回,shiro自動比較用戶名密碼是否匹配哦,進行登陸控制
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authcToken) throws AuthenticationException {
LOGGER.info("Shiro開始登陸認證");
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
User user = userService.findUserByLoginName(token.getUsername());
if(user == null) {
return null;
}
//賬號未啓用
if(user.getStatus() == 1) {
return null;
}
List<Long> roleList = roleService.findRoleIdsByUserId(user.getId());
ShiroUser shiroUser = new ShiroUser(user.getId(), user.getLoginName(), user.getName(),roleList);
//認證緩存器
return new SimpleAuthenticationInfo(shiroUser, user.getPassword().toCharArray(), getName());
}
}
字符串處理工具類:
public class StringEscapeEditor extends PropertyEditorSupport{
private boolean escaoeHTML; //編碼HTML
private boolean escapeJavaScript; //編碼javascript
public StringEscapeEditor(){}
public StringEscapeEditor(boolean escapeHTML, boolean escapeJavaScipt){
this.escaoeHTML = escapeHTML;
this.escapeJavaScript = escapeJavaScipt;
}
@Override
public String getAsText() {
Object value = getValue();
return value != null ? value.toString() : "";
}
@Override
public void setAsText(String text) {
if(text == null) {
setValue(null);
}
else {
String value = text;
if(escaoeHTML) {
value = HtmlUtils.htmlEscape(value);
}
if(escapeJavaScript) {
value = JavaScriptUtils.javaScriptEscape(value);
}
setValue(value);
}
}
}
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<display-name>spring-shiro</display-name>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- springmvc控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet.class</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>
<!-- 是否支持異步 -->
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>DruidWebStatFilter</filter-name>
<filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
<init-param>
<param-name>exclusions</param-name>
<param-value>/static/*,*.js,*.gif,*.png,*.css,*.ico,/druid/*</param-value>
</init-param>
<init-param>
<param-name>sessionStatEnable</param-name>
<param-value>false</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>DruidWebStatFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- shiro過濾 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
<!-- 錯誤頁面 -->
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/views/error/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/views/error/500.jsp</location>
</error-page>
</web-app>