基於SSM框架的博客系統(二)博主登錄功能
一、 準備
1.數據庫
創建表db_blogger:
1 DROP TABLE IF EXISTS `t_blogger`; 2 3 CREATE TABLE `t_blogger` ( 4 5 `id` INT(11) NOT NULL AUTO_INCREMENT, 6 7 `userName` VARCHAR(50) DEFAULT NULL, 8 9 `password` VARCHAR(100) DEFAULT NULL, 10 11 `profile` TEXT, 12 13 `nickName` VARCHAR(50) DEFAULTNULL, 14 15 `sign` VARCHAR(100) DEFAULT NULL, 16 17 `imageName` VARCHAR(100) DEFAULT NULL, 18 19 `salt` VARCHAR(100) DEFAULT NULL, 20 21 PRIMARY KEY (`id`) 22 23 ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 24 25 INSERT INTO `t_blogger`(`id`,`userName`,`password`,`profile`,`nickName`,`sign`,`imageName`,`salt`) VALUES (1,‘neya‘,‘7f50069012f25a74bb2783a2d7100f0e‘,NULL,NULL,NULL,NULL,‘Neya‘);
2.添加shiro支持
1 <!-- 添加shiro支持 --> 2 3 <dependency> 4 5 <groupId>org.apache.shiro</groupId> 6 7 <artifactId>shiro-core</artifactId> 89 <version>1.2.4</version> 10 11 </dependency> 12 13 14 15 <dependency> 16 17 <groupId>org.apache.shiro</groupId> 18 19 <artifactId>shiro-web</artifactId> 20 21 <version>1.2.4</version> 22 23 </dependency> 24 25 26 27 <dependency> 28 29 <groupId>org.apache.shiro</groupId> 30 31 <artifactId>shiro-spring</artifactId> 32 33 <version>1.2.4</version> 34 35 </dependency>
二、 登錄界面及登錄控制
1. 登錄界面
在webapp文件夾下創建login.jsp,以上所需文件及圖片可由以下鏈接下載:鏈接:https://pan.baidu.com/s/1bqCGJld 密碼:701g
登錄界面如下:
2. 創建Realm並配置shiro
Shiro是一個Java平臺的開源權限框架,用於認證和訪問授權。具體來說,滿足對如下元素的支持:
1)用戶,角色,權限(僅僅是操作權限,數據權限必須與業務需求緊密結合),資源(url)。
2)用戶分配角色,角色定義權限。
3)訪問授權時支持角色或者權限,並且支持多級的權限定義。
關於shiro更詳細的信息,推薦閱讀博客:http://jinnianshilongnian.iteye.com/blog/2018398,暫時可以只閱讀前幾章,足夠本項目使用。
下面簡單說一下本項目中所用到的shiro執行流程:前端從login.jsp獲取到用戶提交的用戶名及密碼的表單數據提交到後臺的controller處理,在controller相應的登錄功能模塊中將用戶名和密碼封裝到一個token中(UsernamePasswordToken對象)。在shiro中,應用代碼直接交互的對象是Subject,也就是說shiro的對外API核心就是Subject,其代表了當前“用戶”,這個用戶不一定是具體的人,也可以是與當前應用交互的任何東西。Subject會綁定到SecurityManager,與Subject的所有交互都會委托給SecurityManager。在controller中,通過一個Subject實例通過login方法將之前創建的token提交,後續會運行到自定義的MyRealm類,在此類中由方法doGetAuthenticationInfo實現具體的驗證邏輯,首先需要根據提交的用戶名到數據庫中進行查詢,得到數據庫中相應的密碼及鹽,然後將用戶名、密碼、鹽封裝到一個info(SimpleAuthenticationInfo)中,作為方法的返回值返回,接下來的驗證由shiro進行處理,主要是對比提交的token和info中的信息,將token中的密碼取出與info中取出的鹽使用MD5加密(加密算法及次數在shiro配置文件中配置),得到加密後的字符串與info中的密碼字符串進行匹配,相同則驗證成功,否則拋出異常。
創建包com.neya.shiro,並在其中創建MyRealm類:
1 public class MyRealm extends AuthorizingRealm{ 2 3 4 5 @Override 6 7 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 8 9 10 11 return null; 12 13 } 14 15 16 17 @Override 18 19 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 20 21 // TODO Auto-generated method stub 22 23 24 25 return null; 26 27 } 28 29 30 31 public String getName(){ 32 33 return "myRealm"; 34 35 } 36 37 38 39 }
在resource中創建shiro 配置文件:applicationContext-shiro:
1 <?xml version="1.0" encoding="UTF-8"?> 2 3 <beans xmlns="http://www.springframework.org/schema/beans" 4 5 xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" 6 7 xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 8 9 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 10 11 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd 12 13 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd 14 15 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd 16 17 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd 18 19 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd"> 20 21 22 23 24 25 26 27 <!--securityManage--> 28 29 <!-- 安全管理器 --> 30 31 <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 32 33 <property name="realm" ref="customRealm" /> 34 35 </bean> 36 37 38 39 40 41 <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> 42 43 44 45 <bean id="customRealm" class="com.ssm.shiro.MyRealm"> 46 47 48 49 <property name="credentialsMatcher"> 50 51 <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> 52 53 <!-- 加密方式 --> 54 55 <property name="hashAlgorithmName" value="MD5" /> 56 57 <!-- 加密次數 --> 58 59 <property name="hashIterations" value="1"/> 60 61 </bean> 62 63 </property> 64 65 </bean> 66 67 68 69 <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/> 70 71 <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> 72 73 <property name="securityManager" ref="securityManager"/> 74 75 </bean> 76 77 78 79 80 81 <!--web.xml中shiro的filter對應的bean--> 82 83 <!-- Shiro 的Web過濾器 --> 84 85 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 86 87 <property name="securityManager" ref="securityManager" /> 88 89 <!-- loginUrl認證提交地址,如果沒有認證將會請求此地址進行認證,請求此地址將由formAuthenticationFilter進行表單認證 --> 90 91 <property name="loginUrl" value="/blogger/login.do" /> 92 93 <!-- 認證成功統一跳轉到first.do,建議不配置,不配置的話shiro認證成功會自動到上一個請求路徑 --> 94 95 <property name="filterChainDefinitions"> 96 97 <value> 98 99 100 101 /login=anon 102 103 /admin/**=authc 104 105 106 107 <!-- -/**=authc 表示所有的url都必須認證通過才可以訪問- --> 108 109 110 111 <!--/**=anon 表示所有的url都可以匿名訪問,anon是shiro中一個過濾器的簡寫,關於shiro中的過濾器介紹見--> 112 113 114 115 </value> 116 117 </property> 118 119 </bean> 120 121 122 123 </beans>
3. 登錄控制
使用上一篇中的逆向工程文件generatorConfig.xml將表t_blogger生成相應的實體類及mapper文件,創建相應的Service及實現類:
BloggerService.java:
1 package com.neya.service; 2 3 import com.neya.domain.Blogger; 4 5 public interface BloggerService { 6 7 public Blogger getBloggerByName(String username); 8 9 }
BloggerServiceImpl.java:
1 package com.neya.service.impl; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 5 import org.springframework.stereotype.Service; 6 7 import com.neya.domain.Blogger; 8 9 import com.neya.mapper.BloggerMapper; 10 11 import com.neya.service.BloggerService; 12 13 14 15 @Service 16 17 public class BloggerServiceImp implements BloggerService { 18 19 20 21 @Autowired 22 23 private BloggerMapper bloggerMapper; 24 25 26 27 @Override 28 29 public Blogger getBloggerByName(String username) { 30 31 // TODO Auto-generated method stub 32 33 Blogger blogger=bloggerMapper.getByUserName(username); 34 35 return blogger; 36 37 } 38 39 }
由於逆向工程自動創建的mapper中沒有getByUserName方法,所以需要在BloggerMapper.java中添加此方法並在BloggerMapper.xml中實現:
1 <select id="getByUserName" parameterType="String" resultMap="ResultMapWithBLOBs"> 2 3 select * from t_blogger where userName=#{userName} 4 5 </select>
在MyRealm的doGetAuthenticationInfo方法中添加如下代碼:
1 String username=(String) token.getPrincipal(); 2 3 Blogger blogger=bloggerService.getBloggerByName(username); 4 5 if(blogger!=null){//能查詢到數據則將查詢到的數據封裝到info中與token比較 6 7 SecurityUtils.getSubject().getSession().setAttribute("currentUser", blogger); 8 9 ByteSource credentialsSalt = ByteSource.Util.bytes(blogger.getSalt()); 10 11 AuthenticationInfo info=new SimpleAuthenticationInfo(blogger.getUsername(), blogger.getPassword(),credentialsSalt,getName()); 12 13 return info; 14 15 }else{ 16 17 return null; 18 19 }
前面的login.jsp中,表單提交的地址為"${pageContext.request.contextPath}/blogger/login.do",所以在controller包中創建相應的BloggerController類並使用@RequestMapping註解映射:
package com.neya.controller; import javax.servlet.http.HttpServletRequest; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import com.neya.domain.Blogger; import com.neya.service.BloggerService; @Controller @RequestMapping("/blogger") public class BloggerController { @Autowired private BloggerService bloggerService; @RequestMapping("/login") public String login(Blogger blogger,HttpServletRequest request){//參數使用spring的自動綁定,從blogger中尋找相應的字段與前端提交的表單的‘name‘匹配,相同則會對blogger的屬性直接賦值,比如表單中提交了兩項,name屬性分別為username和password,則在blogger中,若發現有這兩個字段,則直接將提交的數據對這兩個字段賦值 Subject subject=SecurityUtils.getSubject(); UsernamePasswordToken token=new UsernamePasswordToken(blogger.getUsername(),blogger.getPassword());//根據前端提交的信息創建token try{ subject.login(token);//登錄,這裏會執行到MyRealm中的代碼 return "redirect:/admin/main.jsp";//登錄成功則跳轉到相應的後臺管理界面 }catch(Exception e){//驗證失敗會拋出異常,將失敗信息寫回前端 e.printStackTrace(); request.setAttribute("blogger", blogger); request.setAttribute("errorInfo", "用戶名或密碼錯誤"); return "login"; } } }
main.jsp及其所需要的文件在以下鏈接中:
鏈接:https://pan.baidu.com/s/1jJ9K8rO 密碼:l65t
main.jsp放在webapp下的admin文件夾中
另外,在web.xml中還需要加入shiro過濾器
1 <!--在這裏配置shiro的filter--> 2 3 <!-- shiro過慮器,DelegatingFilterProxy通過代理模式將spring容器中的bean和filter關聯起來 --> 4 5 <filter> 6 7 <filter-name>shiroFilter</filter-name> 8 9 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 10 11 <!-- 設置true由servlet容器控制filter的生命周期 --> 12 13 <init-param> 14 15 <param-name>targetFilterLifecycle</param-name> 16 17 <param-value>true</param-value> 18 19 </init-param> 20 21 <!-- 設置spring容器filter的bean id,如果不設置則找與filter-name一致的bean --> 22 23 <init-param> 24 25 <param-name>targetBeanName</param-name> 26 27 <param-value>shiroFilter</param-value> 28 29 </init-param> 30 31 </filter> 32 33 <filter-mapping> 34 35 <filter-name>shiroFilter</filter-name> 36 37 <url-pattern>/*</url-pattern> 38 39 </filter-mapping>
接下來啟動tomcat,並在瀏覽器中輸入http://localhost:8080/MyBlog/login.jsp,填入用戶名neya及密碼123456,點擊登錄即可跳轉到相應後臺管理界面
基於SSM框架的博客系統(二)博主登錄功能