SpringMvc-Httl-shiro的整合

來到新的公司一個月,之前實習公司的用的是srping+hibernate+struts2,而在這裏不在用的這些了,而是用的springMVC和jdbc模板來操做數據了,因此又用了一段時間去慢慢融入這個新的體系中去;但終究這些技術是萬變不離其宗的,學習也是很快的事,因此我也就很快的就融入了這個團隊;html

進入正題吧!我這裏其實就是想把學習新東西本身記錄下來,這有助於個人學習也有助於你們的學習;把springmvc+httl+shiro+maven給整合起來;我剛來到新公司這裏已是搭建好了這個項目;因此我也在空閒時間之餘把這些知識給過了一遍,也有助於我開發效率的提升;java

我使用的開發工具室MyEclipse(開發工具的使用都是大同小異);git

至於httl和shiro的優點我就很少說了有興趣的看:github

http://www.open-open.com/open342321.htm;web

http://blog.csdn.net/boonya/article/details/8233303spring

廢話很少說,開始搭建吧。。。express

1:首先創建一個javaweb項目apache

2:配置pom.xml導入必須的jar包api

<dependencies>
          <!-- junit核心jar包 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!-- springMVC核心jar包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.0.5.RELEASE</version>
        </dependency>

        <!-- httl 核心jar包  -->
        <dependency>
            <groupId>com.github.httl</groupId>
            <artifactId>httl-springmvc</artifactId>
            <version>1.0.10</version>
        </dependency>
        <dependency>
            <groupId>com.github.httl</groupId>
            <artifactId>httl</artifactId>
            <version>1.0.11</version>
        </dependency>
        
        <!--當你運行的環境是jre是時;httl模板必須加入這個jar包;還有就是必須在httl的 httl.properties注入屬性值:
        compiler=httl.spi.compilers.JavassistCompiler。。這點很重要,否則當你啓動tomcat時會一直拋classnotfound異常-->
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.18.2-GA</version>
        </dependency>
        
         <dependency>  
            <groupId>commons-logging</groupId>  
            <artifactId>commons-logging</artifactId>  
            <version>1.1.3</version>  
        </dependency>  
        <!-- shiro -->
        <dependency>  
            <groupId>org.apache.shiro</groupId>  
            <artifactId>shiro-core</artifactId>  
            <version>1.2.2</version>  
        </dependency>  
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.2.2</version>
        </dependency>
            <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-quartz</artifactId>
            <version>1.2.2</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
        
        <!-- spring aop -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.5</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.5</version>
        </dependency>
  </dependencies>

3:修改web.xml文件spring-mvc

<?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" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    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></display-name>
    
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
          classpath:applicationContext-shiro.xml
        </param-value>
    </context-param>
    <!-- 配置Shiro過濾器,先讓Shiro過濾系統接收到的請求 -->
    <!-- 這裏filter-name必須對應applicationContext.xml中定義的<bean id="shiroFilter" /> -->
    <!-- 使用[/*]匹配全部請求,保證全部的可控請求都通過Shiro的過濾 -->
    <!-- 一般會將此filter-mapping放置到最前面(即其餘filter-mapping前面),以保證它是過濾器鏈中第一個起做用的 -->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <!-- 該值缺省爲false,表示生命週期由SpringApplicationContext管理,設置爲true則表示由ServletContainer管理 -->
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    
    <!-- 加載httl的屬性文件 -->
    <context-param>
        <param-name>httl.properties</param-name>
        <param-value>classpath:httl.properties</param-value>
    </context-param>
    
    
    <!-- Character Encoding filter -->
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>


    <!-- Spring MVC Servlet 載入 -->
    <servlet>
        <servlet-name>springMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- 實例化Spring容器 -->
    <!-- 應用啓動時,該監聽器被執行,它會讀取Spring相關配置文件,其默認會到WEB-INF中查找applicationContext.xml -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    


    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

4:添加httl.properties文件

import.packages+=com.lishun.controller

template.directory=/WEB-INF/templates

template.suffix=.httl

input.encoding=utf-8

output.encoding=utf-8

reloadable=true

precompiled=true

compiler=httl.spi.compilers.JavassistCompiler

import.methods+=com.lishun.httl.staticMethods.ShiroKit

 

注:import.packages:領域模型包導入

    import.methods:這裏能夠封裝在httl模板頁面上使用的靜態方法

    template.directory:模板文件目錄;

    template.suffix:模板文件後綴

    input.encoding:資源加載編碼

    output.encoding: 模板輸出編碼

    precompiled:是否預編譯

    reloadable: 是否熱加載;開發模式下建議開啓

    compiler:(摘自httl官網)

     用於將模板類編譯成字節碼,缺省使用根據JDK版本自適應編譯器:(缺省值不用配)

1

compiler=httl.spi.compilers.AdaptiveCompiler

   當前運行環境爲JDK1.6之前版本時,AdaptiveCompiler將適配到JavassistCompiler,不然將適配到JdkCompiler。

   你能夠強制指定使用jdk自帶的編譯器:(必須要用JDK運行,JRE不行,JDK比JRE多編譯工具包)

1

compiler=httl.spi.compilers.JdkCompiler

  你也能夠換成javassist編譯:(若是爲JRE運行,請使用javassist編譯)

1

compiler=httl.spi.compilers.JavassistCompiler

  固然,也就須要增長javassist的jar包依賴:

  javassist-3.15.0-GA.jar

 

 

 

 

<dependency>

    <groupId>org.javassist</groupId>

    <artifactId>javassist</artifactId>

    <version>3.15.0-GA</version>

</dependency>

 

5:編寫applicationContext-shiro.xml 注:該文件是管理shiro的

<!-- 這裏別忘了配置,不然會致使自定義的Realm的屬性值沒法注入 -->
    <context:component-scan base-package="com.lishun"></context:component-scan>
    <!-- 繼承自AuthorizingRealm的自定義Realm,即指定Shiro驗證用戶登陸的類爲自定義的UserRealm.java -->
     <bean id="shiroRealm" class="com.lishun.shiro.realm.UserRealm" >
     
     </bean> 
    
    <!-- 基於Form表單的身份驗證過濾器 -->
    <bean id="formAuthenticationFilter"
        class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
        <property name="usernameParam" value="username" />
        <property name="passwordParam" value="password" />
        <property name="loginUrl" value="/users/index.html" />
        <property name="successUrl" value="/index/index.html" />
    </bean>
    <context:component-scan base-package="com.lishun"></context:component-scan>
    <bean
        class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
        depends-on="lifecycleBeanPostProcessor">
        <property name="proxyTargetClass" value="true" />
    </bean>
    
    <!-- 這裏主要是設置自定義的單Realm應用,如有多個Realm,可以使用'realms'屬性代替 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="shiroRealm" />
    </bean>
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- 調用自定義的權限管理器 -->
        <property name="securityManager" ref="securityManager" />

        <!-- 配置登錄成功後跳轉地址 -->
        <property name="successUrl" value="/index/index" />

        <!-- 配置登錄時請求的地址 -->
        <property name="loginUrl" value="/users/index" />

        <!-- 若是請求的資源再也不你的權限範圍內,則跳轉到error.htm -->
        <property name="unauthorizedUrl" value="/users/noAuth" />

        <property name="filters">
            <map>
                <entry key="authc" value-ref="formAuthenticationFilter"></entry>
            </map>
        </property>
        <property name="filterChainDefinitions">
            <value>
                <!-- anon表示此地址不須要任何權限便可訪問 -->
                /users/*=anon
                <!--login頁面和logout頁面不須要驗證 -->
                /login* = anon
                <!--訪問全部文件,authc必須經過驗證後才能訪問 -->
                /** = authc
            </value>
        </property>
    </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生命週期處理器 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

 

6.編寫spring-mvc.xml文件 注:該文件是管理springmvc的

<!-- 自動掃描且只掃描@Controller -->
    <context:component-scan base-package="com.lishun"
        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>

    <!-- 啓用SpringMVC的註解功能,它會自動註冊HandlerMapping、HandlerAdapter、ExceptionResolver的相關實例 -->
    <mvc:annotation-driven />

    <!-- 配置SpringMVC的視圖解析器 httl視圖 -->
    <bean id="viewResolver" class="httl.web.springmvc.HttlViewResolver">
        <property name="contentType" value="text/html; charset=UTF-8" />
    </bean>

    <!-- 在spring-mvc.xml配置文件添加Shiro Spring AOP權限註解的支持: -->
    <aop:config proxy-target-class="true"></aop:config>
    <bean
        class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager" />
    </bean>
    <!-- 當前用戶沒有權限時跳轉到的頁面: -->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">    
        <property name="exceptionMappings">    
            <props>    
                <prop key="org.apache.shiro.authz.UnauthorizedException">/users/unauthorizedView</prop>  
            </props>    
        </property>    
    </bean>

7:編寫用戶實體(User)和角色實體(Role)

public class User {
    private String name;
    private String password;
    private Role role;
    @Override
    public String toString() {
        return "User [name=" + name + ", password=" + password + ", role="
                + role + "]";
    }
    public String getName() {
        return name;
    }
    public User() {
        super();
    }
    public void setName(String name) {
        this.name = name;
    }
    public User(String name, String password) {
        super();
        this.name = name;
        this.password = password;
    }
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Role getRole() {
        return role;
    }

    public void setRole(Role role) {
        this.role = role;
    }
}
---------------------------------------------------分割線

public class Role {
    private String roleName;
    private Set<String> rolePermissions;
    public Set<String> getRolePermissions() {
        return rolePermissions;
    }
    public void setRolePermissions(Set<String> rolePermissions) {
        this.rolePermissions = rolePermissions;
    }
    public String getRoleName() {
        return roleName;
    }
    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }
}

8:在httl模板上執行靜態函數的類

/*
 * 在httl模板上能夠直接使用的函數
 */
public class ShiroKit {
    /**
     * 禁止初始化
     */
    private ShiroKit() {
    }
    /**
     * 獲取 Subject
     * 
     * @return Subject
     */
    protected static Subject getSubject() {
        return SecurityUtils.getSubject();
    }
    /**
     * 驗證當前用戶是否擁有指定權限,使用時與lacksPermission 搭配使用
     * 
     * @param permission
     *            權限名
     * @return 擁有權限:true,不然false
     */
    public static boolean hasPermission(String permission) {
        boolean ret = getSubject() != null && permission != null && permission.length() > 0 && getSubject().isPermitted(permission);
        return ret;
    }

    /**
     * 與hasPermission標籤邏輯相反,當前用戶沒有制定權限時,驗證經過。
     * 
     * @param permission
     *            權限名
     * @return 擁有權限:true,不然false
     */
    public static boolean lacksPermission(String permission) {
        return !hasPermission(permission);
    }

}

9:自定義用戶驗證規則(必須繼承AuthorizingRealm)

public class UserRealm extends AuthorizingRealm {
    

    @Autowired
    @Qualifier("userDao")
    private UserDao userDao;
    
    
    /*
     * 權限認證
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        // 根據用戶配置用戶與權限
        if (principals == null) {
            throw new AuthorizationException(
                    "PrincipalCollection method argument cannot be null.");
        }
        String name = (String) getAvailablePrincipal(principals);
        List<String> roles = new ArrayList<String>();
        User user = userDao.getUserByName(name);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 把當前用戶的角色保存到Subject中
        roles.add(user.getRole().getRoleName());
        info.addRoles(roles);
        // 把當前用戶的權限保存到Subject中
        info.setStringPermissions(user.getRole().getRolePermissions());
        return info;
    }

    /*
     * 登錄認證
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken authcToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        //獲取頁面輸入用戶名
        String username=(String) token.getPrincipal();
        //從dao層獲取該用戶
        System.out.println(userDao);
        User user = userDao.getUserByName(username);
        if (user == null) {
            throw new AuthorizationException();
        }
        
        SimpleAuthenticationInfo info = null;
        //存儲令牌信息
        if (user.getName().equals(token.getUsername())) {
            info = new SimpleAuthenticationInfo(user.getName(),
                    user.getPassword(), getName());
        }
        return info;
    }
    

}

 

10:編寫dao層

/*
 * 這裏我就不查詢數據數據了,側重點不在這裏,因此寫死用戶數據
 */
public class UserDao {
    
    private static List<User> list=new ArrayList<User>();
    private static Set<String> userPermissions=new HashSet<String>();
    private static Set<String> adminPermissions=new HashSet<String>();
    static {
        //lishun普通用戶
        User lishun =new User();
        lishun.setName("lishun");
        lishun.setPassword("123");
        Role lishunRole=new Role();
        lishunRole.setRoleName("user");
        //lishun普通用戶只擁有一個權限
        userPermissions.add("user:lishun:view");
        lishunRole.setRolePermissions(userPermissions);
        lishun.setRole(lishunRole);
        
        //admin超級用戶
        User admin =new User();
        admin.setName("admin");
        admin.setPassword("123");
        Role adminRole=new Role();
        adminRole.setRoleName("admin");
        //admin用戶擁有全部權限
        adminPermissions.add("user:lishun:view");
        adminPermissions.add("manager:lishun:view");
        adminRole.setRolePermissions(adminPermissions);
        admin.setRole(adminRole);
        list.add(admin);
        list.add(lishun);
    }
    public User getUserByName(String name){
        for (User u : list) {
            if(u.getName().equals(name)){
                return u;
            }
        }
        return null;
    }

11:編寫控制器:用戶登錄控制器

//不用任何權限均可以訪問該控制器的方法
@Controller
public class UserController {

    /*
     * 登錄頁面
     */
    @RequestMapping(value = "users/index", method = RequestMethod.GET)
    public String index() {
        
        return "users/login";
    }
    /*
     * 驗證用戶
     */
    @RequestMapping(value = "users/login", method = RequestMethod.POST)
    public String login(String username,String password,Model model){
        //獲取到當前用戶的的Subject及建立用戶名/密碼身份驗證Token(即用戶身份/憑證)
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        
        String error = null;
        try {
            //驗證登錄
            subject.login(token);
        } catch (AuthenticationException e) {
            error = "warning message:Login failed,please check your username and password"; 
            model.addAttribute("errorMessage", error);
        }
        if (error==null) {        
            return "redirect:../index/index";
        } else {
            return "redirect:index";
        }
    }
    /*
     * 沒有權限後跳轉的頁面
     */
    @RequestMapping(value = "users/unauthorizedView", method = RequestMethod.GET)
    public String unauthorizedView() {
        return "users/unauthorizedView";
    }
}

12:login.httl文件

<!--#set(String msg="httl模板測試") -->
<!--#set(String manageHost = request.getScheme()+"://"+request.serverName+":"+"a".valueOf(request.serverPort)+request.getContextPath())-->

<html>
<body>
    <form action="${manageHost}/users/login" method="post">
        <label style="color: red">${errorMessage}</label>
        <table>
            <tr>
                <td>UserName:</td>
                <td><input type="text" name="username" />
                </td>
            </tr>
            <tr>
                <td>Password:</td>
                <td><input type="password" name="password" />
                </td>
            </tr>
            <tr>
                <td colspan="2"><input type="submit" value="Login" />
                </td>
            </tr>
        </table>
    </form>
</body>
</html> 
-----------------------分隔符

至於httl的語法你們能夠去官網上瀏覽,我這裏就很少說,http://httl.github.io/zh/syntax.html

    打開url:http://localhost:8080/SpringMvc-Httl-shiro/users/login

就能夠瀏覽了

13 編寫控制器:用戶主頁控制器

@Controller
public class IndexController {
    
    /*
     * 普通用戶就能夠訪問該頁面
     */
    @RequiresPermissions("user:lishun:view")
    @RequestMapping(value = "index/index", method = RequestMethod.GET)
    public String index() {
        System.out.println(SecurityUtils.getSubject().isPermitted("user:lishun:view"));
        return "index/index";
    }
}
-------------------------------------------分割線

用戶主頁模板index.httl

<!--#set(String msg="httl模板測試") -->
<!--#set(String manageHost = request.getScheme()+"://"+request.serverName+":"+"a".valueOf(request.serverPort)+request.getContextPath())-->

<html>
<body>
    #if(hasPermission("manager:lishun:view"))
        超級用戶登錄
        <a href="${manageHost}/manager/user">管理用戶</a>
    #else(hasPermission("user:lishun:view"))
        普通用戶登錄
    #end
</body>
</html>

14 編寫控制器:管理員控制器

@Controller
@RequestMapping("manager")
public class ManagerController {

    /*
     * 只有管理員纔有權限查看本頁面
     */
    @RequiresPermissions("manager:lishun:view")
    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public String index() {
        
        return "manager/userManager";
    }
    
}
-------------------------------------------分割線

管理用戶模板userManager.httl

<!--#set(String msg="httl模板測試") -->
<!--#set(String manageHost = request.getScheme()+"://"+request.serverName+":"+"a".valueOf(request.serverPort)+request.getContextPath())-->

<html>
<body>
    <h1>只有admin角色才能夠訪問</h1>
</body>
</html>

當你是普通用戶訪問該頁面時因沒有權限而跳轉到無權限頁面

 因爲本人水平有限,若文章在表述和代碼方面若有不妥之處,歡迎批評指正。留下你的腳印,歡迎評論!但願能互相學習。須要源碼的留下郵箱

最後附上項目的目錄結構

相關文章
相關標籤/搜索