來到新的公司一個月,之前實習公司的用的是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包依賴:
|
<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>
當你是普通用戶訪問該頁面時因沒有權限而跳轉到無權限頁面
因爲本人水平有限,若文章在表述和代碼方面若有不妥之處,歡迎批評指正。留下你的腳印,歡迎評論!但願能互相學習。須要源碼的留下郵箱
最後附上項目的目錄結構