一.初談認證
作爲web系統肯定少不了登錄環節,和spring整合過程中大都通過攔截器來實現,無論是mvc自帶的在xml文件中的配置攔截路徑;或者與本期“主人公”平分秋色的shiro;框架只是給我們提供了一種簡單的實現方式,提高我們的開發速度;但是設計還是我們開發人員需要做的;比如用戶-角色-權限表的相關設計;攔截機制和具體加密實現!
二.代碼實現
1.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_2_5.xsd"
version="2.5">
<!-- 解決post亂碼 -->
<filter>
<filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定加載的配置文件 ,通過參數contextConfigLocation加載-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-security.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
分析:最上面的dispatchServlet是mvc的標準配置(攔截以.do結尾的請求),這裏不做具體敘述;
DelegatingFilterProxy可以看到他不屬於springsecurity特有的;而是springweb相關,由此可見security作爲spring家族的成員,底層貼合的相當好。
springSecurityFilterChain這個名字不是亂起的,源碼規定了它的名字!!!
2.spring-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
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
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
<!-- 設置頁面不登陸也可以訪問 -->
<http pattern="/*.html" security="none"></http>
<http pattern="/css/**" security="none"></http>
<http pattern="/img/**" security="none"></http>
<http pattern="/js/**" security="none"></http>
<http pattern="/plugins/**" security="none"></http>
<http pattern="/seller/add.do" security="none"></http>
<!-- 頁面的攔截規則 use-expressions:是否啓動SPEL表達式 默認是true -->
<http use-expressions="false">
<!-- 當前用戶必須有ROLE_USER的角色 纔可以訪問根目錄及所屬子目錄的資源 -->
<intercept-url pattern="/**" access="ROLE_SELLER"/>
<!-- 開啓表單登陸功能 -->
<form-login login-page="/shoplogin.html" default-target-url="/admin/index.html" authentication-failure-url="/shoplogin.html" always-use-default-target="true"/>
<csrf disabled="true"/>
<headers>
<frame-options policy="SAMEORIGIN"/>
</headers>
<logout/>
</http>
<!-- 認證管理器 -->
<authentication-manager>
<authentication-provider user-service-ref="userDetailService">
<password-encoder ref="bcryptEncoder"></password-encoder>
</authentication-provider>
</authentication-manager>
<!-- 認證類 -->
<beans:bean id="userDetailService" class="com.pinyougou.service.UserDetailsServiceImpl">
<!--<beans:property name="sellerService" ref="sellerService"></beans:property>-->
</beans:bean>
<!-- 引用dubbo 服務 -->
<!--<dubbo:application name="pinyougou-shop-web" />-->
<!--<dubbo:registry address="zookeeper://119.23.229.151:2181"/>-->
<!--<dubbo:reference id="sellerService" interface="com.pinyougou.sellergoods.service.SellerService"></dubbo:reference>-->
<beans:bean id="bcryptEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></beans:bean>
</beans:beans>
<intercept-url pattern="/**" access="ROLE_SELLER"/>
這表示系統會對所有ROOT目錄下面的資源進行攔截;請求必須爲ROLE_SELLER角色;(/*只能攔截一層;/**攔截當前及其所有子目錄);
對於靜態資源我們希望對其放行,所以要將他們排除在外:
<http pattern="/*.html" security="none"></http>
<http pattern="/css/**" security="none"></http>
<http pattern="/img/**" security="none"></http>
<http pattern="/js/**" security="none"></http>
<http pattern="/plugins/**" security="none"></http>
表示是否使用spel<http use-expressions="false">
,默認爲true,那麼我們下面的配置需要設置成這樣access="hasRole(‘ROLE_SELLER’)"
<form-login login-page="/shoplogin.html" default-target-url="/admin/index.html" authentication-failure-url="/shoplogin.html" always-use-default-target="true"/>
定義提交的表單:
default-target-url表示登錄成功的默認界面
authentication-failure-url登錄失敗的界面
always-use-default-target="true"如果登錄成功跳轉到用戶想要請求的路徑
<csrf disabled="true"/>
這裏不得不提我們web網站經常會出現的安全問題;
CSRF(Cross-site request forgery)跨站請求僞造,也被稱爲“One Click Attack”或者Session Riding,通常縮寫爲CSRF或者XSRF,是一種對網站的惡意利用。儘管聽起來像跨站腳本(XSS),但它與XSS非常不同,XSS利用站點內的信任用戶,而CSRF則通過僞裝成受信任用戶的請求來利用受信任的網站。與XSS攻擊相比,CSRF攻擊往往不大流行(因此對其進行防範的資源也相當稀少)和難以防範,所以被認爲比XSS更具危險性。
主要由於請求發生時需要攜帶X-token;我們只有在jsp才能做相應的處理。所以這裏將其設爲不可用;負責登錄回報403錯誤
<headers>
<frame-options policy="SAMEORIGIN"/>
</headers>
這個是我的系統裏面需要用到frame;如果不這麼設置;target內容沒辦法展示出來。
最主要的部分來了!
<authentication-manager>
<authentication-provider user-service-ref="userDetailService">
<password-encoder ref="bcryptEncoder"></password-encoder>
</authentication-provider>
</authentication-manager>
bcryptEncoder是我們定義的加密算法,這個算法每次產生新密碼都是不一樣的。
userDetailService是我們定義的;來作爲認證主體
3.userDetailService代碼
package com.pinyougou.service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.alibaba.dubbo.config.annotation.Reference;
import com.pinyougou.pojo.TbSeller;
import com.pinyougou.sellergoods.service.SellerService;
/**
* 認證類
* @author Administrator
*
*/
public class UserDetailsServiceImpl implements UserDetailsService {
@Reference
private SellerService sellerService;
// public void setSellerService(SellerService sellerService) {
// this.sellerService = sellerService;
// }
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("經過了UserDetailsServiceImpl");
//構建角色列表
List<GrantedAuthority> grantAuths=new ArrayList();
grantAuths.add(new SimpleGrantedAuthority("ROLE_SELLER"));
//得到商家對象
TbSeller seller = sellerService.findOne(username);
if(seller!=null){
if(seller.getStatus().equals("1")){
return new User(username,seller.getPassword(),grantAuths);
}else{
return null;
}
}else{
return null;
}
}
}
這裏的SellerService通過註解或者set方法都可以(@Reference是dubbo的遠程rpc注入註解;簡單項目使用autowired效果一樣)。
四.總結
權限框架本身基於代理對象實現,理解起來並不難;上手也比較簡單;
作爲開發者,你保證你使用這個框架在線上不會出現太大的問題或者發生的問題都能立刻解決,這就夠了!!!