簡單權限系統基於shiro-springmvc-spring-mybatis(學習筆記2)

學習筆記(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>
發佈了69 篇原創文章 · 獲贊 20 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章