詳解spring security四種實(shí)現(xiàn)方式
spring security實(shí)現(xiàn)方式大致可以分為這幾種:
1.配置文件實(shí)現(xiàn),只需要在配置文件中指定攔截的url所需要權(quán)限、配置userDetailsService指定用戶名、密碼、對(duì)應(yīng)權(quán)限,就可以實(shí)現(xiàn)。
2.實(shí)現(xiàn)UserDetailsService,loadUserByUsername(String userName)方法,根據(jù)userName來(lái)實(shí)現(xiàn)自己的業(yè)務(wù)邏輯返回UserDetails的實(shí)現(xiàn)類,需要自定義User類實(shí)現(xiàn)UserDetails,比較重要的方法是getAuthorities(),用來(lái)返回該用戶所擁有的權(quán)限。
3.通過(guò)自定義filter重寫spring security攔截器,實(shí)現(xiàn)動(dòng)態(tài)過(guò)濾用戶權(quán)限。
4.通過(guò)自定義filter重寫spring security攔截器,實(shí)現(xiàn)自定義參數(shù)來(lái)檢驗(yàn)用戶,并且過(guò)濾權(quán)限。
1.最簡(jiǎn)單配置spring-security.xml,實(shí)現(xiàn)1
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:p="http://www.springframework.org/schema/p" 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-4.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.0.xsd">
<!-- use-expressions:Spring 表達(dá)式語(yǔ)言配置訪問控制 -->
<security:http auto-config="true" use-expressions="false">
<!-- 配置權(quán)限攔截,訪問所有url,都需要用戶登錄,且擁有ROLE_USER權(quán)限 -->
<security:intercept-url pattern="/**" access="ROLE_USER" />
</security:http>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider>
<!-- 配置默認(rèn)用戶,用戶名:admin 密碼:123456 擁有權(quán)限:ROLE_USER -->
<security:user-service>
<security:user name="admin" password="123456"
authorities="ROLE_USER" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
</beans>
2.實(shí)現(xiàn)UserDetailsService
先整理下spring secruity驗(yàn)證流程:
springSecurity的登錄驗(yàn)證是由org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter這個(gè)過(guò)濾器來(lái)完成的,在該類的父類AbstractAuthenticationProcessingFilter中有一個(gè)AuthenticationManager接口屬性,驗(yàn)證工作主要是通過(guò)這個(gè)AuthenticationManager接口的實(shí)例來(lái)完成的。在默認(rèn)情況下,springSecurity框架會(huì)把org.springframework.security.authentication.ProviderManager類的實(shí)例注入到該屬性
UsernamePasswordAuthenticationFilter的驗(yàn)證過(guò)程如下:
1. 首先過(guò)濾器會(huì)調(diào)用自身的attemptAuthentication方法,從request中取出authentication, authentication是在org.springframework.security.web.context.SecurityContextPersistenceFilter過(guò)濾器中通過(guò)捕獲用戶提交的登錄表單中的內(nèi)容生成的一個(gè)org.springframework.security.core.Authentication接口實(shí)例.
2. 拿到authentication對(duì)象后,過(guò)濾器會(huì)調(diào)用ProviderManager類的authenticate方法,并傳入該對(duì)象
3.ProviderManager類的authenticate方法中會(huì)調(diào)用類中的List<AuthenticationProvider> providers集合中的各個(gè)AuthenticationProvider接口實(shí)現(xiàn)類中的authenticate(Authentication authentication)方法進(jìn)行驗(yàn)證,由此可見,真正的驗(yàn)證邏輯是由各個(gè)AuthenticationProvider接口實(shí)現(xiàn)類來(lái)完成的。DaoAuthenticationProvider類是默認(rèn)情況下注入的一個(gè)AuthenticationProvider接口實(shí)現(xiàn)類
4.provider的實(shí)現(xiàn)類在驗(yàn)證用戶時(shí),會(huì)調(diào)用userDetailsService的實(shí)現(xiàn)類的loadUserByUsername方法來(lái)獲取用戶信息,
首先spring-security配置文件
<?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:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<!-- use-expressions=”true” 需要使用表達(dá)式方式來(lái)寫權(quán)限-->
<http auto-config="true" use-expressions="false">
<!--這是spring 提供的http/https信道安全的這個(gè)是重要的!你的請(qǐng)求信道是安全的!-->
<!--
釋放用戶登陸page 允許任何人訪問該頁(yè)面 ,IS_AUTHENTICATED_ANONYMOUSLY表示不攔截
另一種不攔截資源的配置:<http pattern="/login.jsp" security="none">
-->
<intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<!-- 配置用戶正常訪問page-->
<intercept-url pattern="/**" access="ROLE_USER"/>
<!-- 自定義用戶登陸page default-target-url登陸成功跳轉(zhuǎn)的page ,authentication-failure-url="/login.jsp?error=true"這里是登陸失敗跳轉(zhuǎn)的page-->
<form-login login-page="/login.jsp" default-target-url="/jsp/index/main.jsp" authentication-failure-url="/login.jsp?error=true"/>
<!-- 記住密碼 -->
<!-- <remember-me key="elim" user-service-ref="securityManager"/> -->
</http>
<authentication-manager alias="authenticationManager">
<!--
authentication-provider 引用UserDetailsService實(shí)現(xiàn)類時(shí)使用user-service-ref屬性,引用authentication實(shí)現(xiàn)類時(shí),使用ref屬性
這兩個(gè)屬性的區(qū)別在于
ref:直接將ref依賴的bean注入到AuthenticationProvider的providers集合中
user-service-ref:定義DaoAuthenticationProvider的bean注入到AuthenticationProvider的providers集合中,
并且DaoAuthenticationProvider的變量userDetailsService由user-service-ref依賴的bean注入。
-->
<authentication-provider user-service-ref="msecurityManager">
<!-- 密碼加密 -->
<password-encoder ref="myPasswordEncoder"/>
</authentication-provider>
</authentication-manager>
<!-- 實(shí)現(xiàn)UserDetailsService -->
<beans:bean id="msecurityManager" class="com.ultrapower.me.util.security.support.SecurityManagerSupport"></beans:bean>
<!-- 密碼加密 -->
<beans:bean id="myPasswordEncoder" class="com.ultrapower.me.util.security.MyPasswordEncoder"/>
</beans:beans>
userDetailsService實(shí)現(xiàn):
/**
*
*/
package com.ultrapower.me.util.security.support;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.ultrapower.me.util.Constants;
import com.ultrapower.me.util.dbDao.SpringBeanUtil;
import com.ultrapower.me.util.security.SecurityManager;
import com.ultrapower.me.util.security.entity.Resource;
import com.ultrapower.me.util.security.entity.Role;
import com.ultrapower.me.util.security.entity.User;
import com.ultrapower.me.util.task.PasswordUtils;
public class SecurityManagerSupport implements UserDetailsService{
private Log log = LogFactory.getLog(this.getClass().getName());
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException {
// List<User> users = getHibernateTemplate().find("FROM User user WHERE user.name = ? AND user.disabled = false", userName);
log.info("SecurityManagerSupport.loadUserByUsername.userName:"+userName);
User user =null;
if("admin".equals(userName)){
Set<Role> roles = new HashSet<Role>() ;
Role role = new Role();
role.setRoleid("ROLE_USER");
role.setRoleName("ROLE_USER");
Set<Resource> resources=new HashSet<Resource>() ;
Resource res = new Resource();
res.setResid("ME001");
res.setResName("首頁(yè)");
res.setResUrl("/jsp/index/main.jsp");
res.setType("ROLE_USER");
res.setRoles(roles);
resources.add(res);
role.setResources(resources);
roles.add(role);
user = new User();
user.setAccount("admin");
user.setDisabled(false);
user.setPassword(PasswordUtils.entryptPassword(Constants.securityKey));
log.info(user.getPassword());
user.setRoles(roles);
}
return user;//返回UserDetails的實(shí)現(xiàn)user不為空,則驗(yàn)證通過(guò)
}
}
UserDetails實(shí)現(xiàn):
/**
*
*/
package com.ultrapower.me.util.security.entity;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class User implements UserDetails {
private static final long serialVersionUID = 8026813053768023527L;
private String account;
private String name;
private String password;
private boolean disabled;
private Set<Role> roles;
private Map<String, List<Resource>> roleResources;
/**
* The default constructor
*/
public User() {
}
/**
* Returns the authorites string
*
* eg.
* downpour --- ROLE_ADMIN,ROLE_USER
* robbin --- ROLE_ADMIN
*
* @return
*/
public String getAuthoritiesString() {
List<String> authorities = new ArrayList<String>();
for(GrantedAuthority authority : this.getAuthorities()) {
authorities.add(authority.getAuthority());
}
return StringUtils.join(authorities, ",");
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// 根據(jù)自定義邏輯來(lái)返回用戶權(quán)限,如果用戶權(quán)限返回空或者和攔截路徑對(duì)應(yīng)權(quán)限不同,驗(yàn)證不通過(guò)
if(!roles.isEmpty()){
List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
GrantedAuthority au = new SimpleGrantedAuthority("ROLE_USER");
list.add(au);
return list;
}
return null;
}
/*
* 密碼
*/
public String getPassword() {
return password;
}
/*
* 用戶名
*/
public String getUsername() {
return name;
}
/*
*帳號(hào)是否不過(guò)期,false則驗(yàn)證不通過(guò)
*/
public boolean isAccountNonExpired() {
return true;
}
/*
* 帳號(hào)是否不鎖定,false則驗(yàn)證不通過(guò)
*/
public boolean isAccountNonLocked() {
return true;
}
/*
* 憑證是否不過(guò)期,false則驗(yàn)證不通過(guò)
*/
public boolean isCredentialsNonExpired() {
return true;
}
/*
* 該帳號(hào)是否啟用,false則驗(yàn)證不通過(guò)
*/
public boolean isEnabled() {
return !disabled;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @return the disabled
*/
public boolean isDisabled() {
return disabled;
}
/**
* @return the roles
*/
public Set<Role> getRoles() {
return roles;
}
/**
* @return the roleResources
*/
public Map<String, List<Resource>> getRoleResources() {
// init roleResources for the first time
System.out.println("---------------------------------------------------");
if(this.roleResources == null) {
this.roleResources = new HashMap<String, List<Resource>>();
for(Role role : this.roles) {
String roleName = role.getRoleName();
Set<Resource> resources = role.getResources();
for(Resource resource : resources) {
String key = roleName + "_" + resource.getType();
if(!this.roleResources.containsKey(key)) {
this.roleResources.put(key, new ArrayList<Resource>());
}
this.roleResources.get(key).add(resource);
}
}
}
return this.roleResources;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @param password the password to set
*/
public void setPassword(String password) {
this.password = password;
}
/**
* @param disabled the disabled to set
*/
public void setDisabled(boolean disabled) {
this.disabled = disabled;
}
/**
* @param roles the roles to set
*/
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public void setRoleResources(Map<String, List<Resource>> roleResources) {
this.roleResources = roleResources;
}
}
3.實(shí)現(xiàn)動(dòng)態(tài)過(guò)濾用戶權(quán)限
在spring-security配置文件的http標(biāo)簽中添加如下配置
<custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="securityInterceptor"/>
在spring-security配置文件中添加如下配置
<!-- 自定義攔截器 --> <beans:bean id="securityInterceptor" class="com.ultrapower.me.util.security.interceptor.SecurityInterceptor"> <beans:property name="authenticationManager" ref="authenticationManager"/> <beans:property name="accessDecisionManager" ref="mesecurityAccessDecisionManager"/> <beans:property name="securityMetadataSource" ref="secureResourceFilterInvocationDefinitionSource" /> </beans:bean> <!-- 獲取訪問url對(duì)應(yīng)的所有權(quán)限 --> <beans:bean id="secureResourceFilterInvocationDefinitionSource" class="com.ultrapower.me.util.security.interceptor.SecureResourceFilterInvocationDefinitionSource" /> <!-- 校驗(yàn)用戶的權(quán)限是否足夠 --> <beans:bean id="mesecurityAccessDecisionManager" class="com.ultrapower.me.util.security.interceptor.SecurityAccessDecisionManager" />
securityInterceptor繼承AbstractSecurityInterceptor過(guò)濾器,實(shí)現(xiàn)Filter過(guò)濾器
package com.ultrapower.me.util.security.interceptor;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
public class SecurityInterceptor extends AbstractSecurityInterceptor implements Filter{
//配置文件注入
private FilterInvocationSecurityMetadataSource securityMetadataSource;
public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
return securityMetadataSource;
}
public void setSecurityMetadataSource(
FilterInvocationSecurityMetadataSource securityMetadataSource) {
this.securityMetadataSource = securityMetadataSource;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// TODO Auto-generated method stub\
FilterInvocation fi = new FilterInvocation(request, response, chain);
//fi里面有一個(gè)被攔截的url
//里面調(diào)用MyInvocationSecurityMetadataSource的getAttributes(Object object)這個(gè)方法獲取fi對(duì)應(yīng)的所有權(quán)限
//再調(diào)用MyAccessDecisionManager的decide方法來(lái)校驗(yàn)用戶的權(quán)限是否足夠
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
//執(zhí)行下一個(gè)攔截器
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public Class<?> getSecureObjectClass() {
// TODO Auto-generated method stub
return FilterInvocation.class;
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
// TODO Auto-generated method stub
return this.securityMetadataSource;
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
登陸后,每次訪問資源都會(huì)被這個(gè)攔截器攔截,會(huì)執(zhí)行doFilter這個(gè)方法,這個(gè)方法調(diào)用了invoke方法,其中fi斷點(diǎn)顯示是一個(gè)url(可能重寫了toString方法吧,但是里面還有一些方法的),最重要的是beforeInvocation這個(gè)方法,它首先會(huì)調(diào)用MyInvocationSecurityMetadataSource類的getAttributes方法獲取被攔截url所需的權(quán)限,在調(diào)用MyAccessDecisionManager類decide方法判斷用戶是否夠權(quán)限。弄完這一切就會(huì)執(zhí)行下一個(gè)攔截器。
secureResourceFilterInvocationDefinitionSource實(shí)現(xiàn)
/**
*
*/
package com.ultrapower.me.util.security.interceptor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
public class SecureResourceFilterInvocationDefinitionSource implements FilterInvocationSecurityMetadataSource, InitializingBean {
private PathMatcher matcher;
private static Map<String, Collection<ConfigAttribute>> map = new HashMap<String, Collection<ConfigAttribute>>();
/*
* 初始化用戶權(quán)限,為了簡(jiǎn)便操作沒有從數(shù)據(jù)庫(kù)獲取
* 實(shí)際操作可以從數(shù)據(jù)庫(kù)中獲取所有資源路徑url所對(duì)應(yīng)的權(quán)限
*/
public void afterPropertiesSet() throws Exception {
this.matcher = new AntPathMatcher();//用來(lái)匹配訪問資源路徑
Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
ConfigAttribute ca = new SecurityConfig("ROLE_USER");
atts.add(ca);
map.put("/jsp/index/main.jsp", atts);
Collection<ConfigAttribute> attsno =new ArrayList<ConfigAttribute>();
ConfigAttribute cano = new SecurityConfig("ROLE_NO");
attsno.add(cano);
map.put("/http://blog.csdn.net/u012367513/article/details/other.jsp", attsno);
}
@Override
public Collection<ConfigAttribute> getAttributes(Object object)
throws IllegalArgumentException {
// TODO Auto-generated method stub
FilterInvocation filterInvocation = (FilterInvocation) object;
String requestURI = filterInvocation.getRequestUrl();
//循環(huán)資源路徑,當(dāng)訪問的Url和資源路徑url匹配時(shí),返回該Url所需要的權(quán)限
for(Iterator<Map.Entry<String, Collection<ConfigAttribute>>> iter = map.entrySet().iterator(); iter.hasNext();) {
Map.Entry<String, Collection<ConfigAttribute>> entry = iter.next();
String url = entry.getKey();
if(matcher.match(url, requestURI)) {
return map.get(requestURI);
}
}
return null;
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
// TODO Auto-generated method stub
return null;
}
/* (non-Javadoc)
* @see org.springframework.security.intercept.ObjectDefinitionSource#getConfigAttributeDefinitions()
*/
@SuppressWarnings("rawtypes")
public Collection getConfigAttributeDefinitions() {
return null;
}
/* (non-Javadoc)
* @see org.springframework.security.intercept.ObjectDefinitionSource#supports(java.lang.Class)
*/
public boolean supports(@SuppressWarnings("rawtypes") Class clazz) {
return true;
}
/**
*
* @param filterInvocation
* @return
*/
@SuppressWarnings("unchecked")
private Map<String, String> getUrlAuthorities(org.springframework.security.web.FilterInvocation filterInvocation) {
ServletContext servletContext = filterInvocation.getHttpRequest().getSession().getServletContext();
return (Map<String, String>)servletContext.getAttribute("urlAuthorities");
}
}
mesecurityAccessDecisionManager實(shí)現(xiàn)
package com.ultrapower.me.util.security.interceptor;
import java.util.Collection;
import java.util.Iterator;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
public class SecurityAccessDecisionManager implements AccessDecisionManager {
/**
* 檢查用戶是否夠權(quán)限訪問資源
* authentication 是從spring的全局緩存SecurityContextHolder中拿到的,里面是用戶的權(quán)限信息
* object 是url
* configAttributes 所需的權(quán)限
* @see org.springframework.security.access.AccessDecisionManager#decide(org.springframework.security.core.Authentication, java.lang.Object, java.util.Collection)
*/
@Override
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
// 對(duì)應(yīng)url沒有權(quán)限時(shí),直接跳出方法
if(configAttributes == null){
return;
}
Iterator<ConfigAttribute> ite=configAttributes.iterator();
//判斷用戶所擁有的權(quán)限,是否符合對(duì)應(yīng)的Url權(quán)限,如果實(shí)現(xiàn)了UserDetailsService,則用戶權(quán)限是loadUserByUsername返回用戶所對(duì)應(yīng)的權(quán)限
while(ite.hasNext()){
ConfigAttribute ca=ite.next();
String needRole=((SecurityConfig)ca).getAttribute();
for(GrantedAuthority ga : authentication.getAuthorities()){
System.out.println(":::::::::::::"+ga.getAuthority());
if(needRole.equals(ga.getAuthority())){
return;
}
}
}
//注意:執(zhí)行這里,后臺(tái)是會(huì)拋異常的,但是界面會(huì)跳轉(zhuǎn)到所配的access-denied-page頁(yè)面
throw new AccessDeniedException("no right");
}
@Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
4.實(shí)現(xiàn)AuthenticationProvider,自定義參數(shù)驗(yàn)證
這種驗(yàn)證以前項(xiàng)目用過(guò),現(xiàn)在沒有寫示例代碼,先寫下大概流程和需要用到的類
這種驗(yàn)證的好處:可以在自定義登錄界面添加登錄時(shí)需要的參數(shù),如多個(gè)驗(yàn)證碼等、可以修改默認(rèn)登錄名稱和密碼的參數(shù)名
整體流程:
1.用戶登錄時(shí),先經(jīng)過(guò)自定義的passcard_filter過(guò)濾器,該過(guò)濾器繼承了AbstractAuthenticationProcessingFilter,并且綁定了登錄失敗和成功時(shí)需要的處理器(跳轉(zhuǎn)頁(yè)面使用)
2.執(zhí)行attemptAuthentication方法,可以通過(guò)request獲取登錄頁(yè)面?zhèn)鬟f的參數(shù),實(shí)現(xiàn)自己的邏輯,并且把對(duì)應(yīng)參數(shù)set到AbstractAuthenticationToken的實(shí)現(xiàn)類中
3.驗(yàn)證邏輯走完后,調(diào)用 this.getAuthenticationManager().authenticate(token);方法,執(zhí)行AuthenticationProvider的實(shí)現(xiàn)類的supports方法
4.如果返回true則繼續(xù)執(zhí)行authenticate方法
5.在authenticate方法中,首先可以根據(jù)用戶名獲取到用戶信息,再者可以拿自定義參數(shù)和用戶信息做邏輯驗(yàn)證,如密碼的驗(yàn)證
6.自定義驗(yàn)證通過(guò)以后,獲取用戶權(quán)限set到User中,用于springSecurity做權(quán)限驗(yàn)證
7.this.getAuthenticationManager().authenticate(token)方法執(zhí)行完后,會(huì)返回Authentication,如果不為空,則說(shuō)明驗(yàn)證通過(guò)
8.驗(yàn)證通過(guò)后,可實(shí)現(xiàn)自定義邏輯操作,如記錄cookie信息
9.attemptAuthentication方法執(zhí)行完成后,由springSecuriy來(lái)進(jìn)行對(duì)應(yīng)權(quán)限驗(yàn)證,成功于否會(huì)跳轉(zhuǎn)到相對(duì)應(yīng)處理器設(shè)置的界面。
1.自定義PassCardAuthenticationToken類,繼承AbstractAuthenticationToken類,用于定義參數(shù),需要實(shí)現(xiàn)的方法
/**
* 憑證,用戶密碼
*/
@Override
public Object getCredentials() {
return password;
}
/**
* 當(dāng)事人,登錄名 用戶Id
*/
@Override
public Object getPrincipal() {
return userID;
}
2.User類要實(shí)現(xiàn)Authentication,需要實(shí)現(xiàn)的方法
/**
* 返回用戶所屬權(quán)限
*/
@Override
public Collection<GrantedAuthority> getAuthorities() {
return this.accesses;
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getDetails() {
return null;
}
/**
* 登錄名稱
*/
@Override
public Object getPrincipal() {
return loginName;
}
/**
* 是否認(rèn)證
*/
@Override
public boolean isAuthenticated() {
return this.authenticated;
}
/**
* 設(shè)置是否認(rèn)證字段
*/
@Override
public void setAuthenticated(boolean isAuthenticated)
throws IllegalArgumentException {
this.authenticated=isAuthenticated;
}
3.需要userService實(shí)現(xiàn)AuthenticationProvider的 authenticate(Authentication authentication)方法
@SuppressWarnings("unchecked")
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
PassCardAuthenticationToken token=(PassCardAuthenticationToken)authentication;
/*
* 這里進(jìn)行邏輯認(rèn)證操作,可以獲取token中的屬性來(lái)自定義驗(yàn)證邏輯,代碼驗(yàn)證邏輯可以不用管
* 如果使用UserDetailsService的實(shí)現(xiàn)類來(lái)驗(yàn)證,就只能獲取userName,不夠靈活
*/
if(token.getUserID()!=null&&token.getPassword()!=null){
User user=(User)this.getDao().executeQueryUnique("User.loadByLoginName", QueryCmdType.QUERY_NAME, token.getUserID());
String password=token.getPassword();
if(this.passwordEncoder!=null){
password=this.passwordEncoder.encodePassword(password, null);
}
if(!password.equalsIgnoreCase(user.getPassword())){
token.setErrCode("2");
return null;
}
if( token.isEnablePasscard() && usePassCard ){//token中激活密碼卡且系統(tǒng)使用密碼卡
int position1=((token.getRow1()-1)*7)+token.getColumn1();
int position2=((token.getRow2()-1)*7)+token.getColumn2();
//System.out.println( "---pos:"+position1+"---"+position2 );
if(user.getPassCardId()==null){
token.setErrCode("10");
return null;
}
PassCard passcard=this.passCardDao.findById(user.getPassCardId(), false);
if(passcard==null||passcard.getStatus()==PassCardHelper.STATUS_CANCEL ){
token.setErrCode("10");
return null;
}
if(passcard.getConfusedContent()==null || passcard.getConfusedContent().length()<7*7*32 ){
token.setErrCode("10");
return null;
}
String content=passcard.getConfusedContent();
int perLen=content.length()/49;
String str1=content.substring((position1-1)*perLen, position1*perLen);
String str2=content.substring((position2-1)*perLen, position2*perLen);
String inputStr1=token.getCard1();
String inputStr2=token.getCard2();
if(this.passwordEncoder!=null){
inputStr1 = md5.getMD5ofStr(md5.getMD5ofStr(inputStr1));
inputStr2 = md5.getMD5ofStr(md5.getMD5ofStr(inputStr2));
}
if((!str1.equalsIgnoreCase(inputStr1))||(!str2.equalsIgnoreCase(inputStr2))){
token.setErrCode("10");
return null;
}
}
user.setLastIp(token.getIp());
user.setLastLogin(new Date());
this.getDao().saveOrUpdate(user);
user.setAuthenticated(true);
/*
* 導(dǎo)入一次角色權(quán)限,并且把權(quán)限set到User中,用于spring驗(yàn)證用戶權(quán)限(getAuthorities方法)
*/
List<UserRole> userRoles=(List<UserRole>)this.getDao().executeQueryList("UserRole.listRoleByUserID", QueryCmdType.QUERY_NAME, -1, -1, user.getId());
Set<GrantedAuthority> accesses=new HashSet<GrantedAuthority>();
for(UserRole ur:userRoles){
accesses.add(ur.getRole());
}
user.getOrg().getOrgName();
if(user.getOrg().getCertTypes()!=null) user.getOrg().getCertTypes().size();//延遲載入一下
user.setAccesses(accesses);
return user;
}
return null;
}
重寫supports(Class<? extends Object> authentication)方法,authentication要
/**
* 如果此處驗(yàn)證不通過(guò),是不會(huì)執(zhí)行authentication方法的
*/
@Override
public boolean supports(Class<? extends Object> authentication) {
return authentication.equals(PassCardAuthenticationToken.class);
}
4.定義filter,實(shí)現(xiàn)AbstractAuthenticationProcessingFilter的attemptAuthentication方法,用于獲取在登錄頁(yè)面?zhèn)鬟f過(guò)來(lái)的參數(shù),spring默認(rèn)只獲取userName(j_username),password(j_username),而且實(shí)現(xiàn)UserDetailsService時(shí)只傳遞username
import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.log4j.Logger;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.util.StringUtils;
import cn.edu.jszg.cert.user.UserLog;
import cn.edu.jszg.cert.user.UserLogService;
import cn.edu.jszg.cert.web.WebApplicationConfiguration;
import cn.edu.jszg.cert.web.controller.portal.auth.RemoteDataValidator;
import com.google.code.kaptcha.servlet.KaptchaServlet;
public class PasscardAuthenticationProcessingFilter extends
AbstractAuthenticationProcessingFilter {
private String successPage = "/home/admin/index";
private String failurePage = "/public/adminLoginEntry";
private boolean forward = false;
private boolean useVerifyCode=true;
private String certLoginUrl;
static Logger logger = Logger.getLogger(PasscardAuthenticationProcessingFilter.class);
private WebApplicationConfiguration config;
private UserLogService userLogService;
public void setConfig(WebApplicationConfiguration config) {
this.config = config;
}
/**
* 實(shí)現(xiàn)AbstractAuthenticationProcessingFilter的有參構(gòu)造
* 沒記錯(cuò)的話,相當(dāng)于該filter的訪問路徑
*/
protected PasscardAuthenticationProcessingFilter() {
super("/adminLoginCheck");
}
public void setUseVerifyCode(boolean useVerifyCode) {
this.useVerifyCode = useVerifyCode;
}
public void setUserLogService(UserLogService userLogService) {
this.userLogService = userLogService;
}
public boolean validate(HttpServletRequest request) {
String userId = request.getParameter("username");
String md2 = request.getParameter("m");
String l = request.getParameter("l");
if (userId == null || md2 == null || l == null) {
return false;
}
long longTime = Long.parseLong(l);
if (longTime < new Date().getTime()) {
return false;
}
try {
String md1 = RemoteDataValidator.genExamMd5Digest(userId, longTime);
if (md1.equals(md2))
return true;
} catch (Exception e) {
//e.printStackTrace();
}
return false;
}
/**
* 可以通過(guò)request獲取頁(yè)面?zhèn)鬟f過(guò)來(lái)的參數(shù),并且set到相應(yīng)的token中
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException,
IOException, ServletException {
// logger.warn("-----------------start證書登錄用戶----------");
HttpSession s = request.getSession(true);
PassCardAuthenticationToken token = new PassCardAuthenticationToken();
String verifyCode = request.getParameter("verifyCode");
String userID = request.getParameter("username");
//....此處省略獲取參數(shù),并且驗(yàn)證、賦值的邏輯
Authentication auth = null;
try {
//此處調(diào)用getAuthenticationManager的authenticate方法,當(dāng)supports方法返回true時(shí)執(zhí)行authenticate方法
auth = this.getAuthenticationManager().authenticate(token);
//此處為登錄成功后,相應(yīng)的處理邏輯
if (auth == null || !auth.isAuthenticated()) {
s.setAttribute("__login_error", token.getErrCode());
} else {
s.removeAttribute("__login_error");
s.removeAttribute("__login_username");
s.removeAttribute("__cert_userid");
if( token.isEnablePasscard()) {
s.removeAttribute("__passcard_row1");
s.removeAttribute("__passcard_row2");
s.removeAttribute("__passcard_column1");
s.removeAttribute("__passcard_column2");
}
}
} catch (AuthenticationException e) {
s.setAttribute("__login_error", token.getErrCode());
throw e;
}
return auth;
}
public void setSuccessPage(String successPage) {
this.successPage = successPage;
}
public void setFailurePage(String failurePage) {
this.failurePage = failurePage;
}
public void setForward(boolean forward) {
this.forward = forward;
}
public void setCertLoginUrl(String certLoginUrl) {
this.certLoginUrl = certLoginUrl;
}
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
/*
*該處理器實(shí)現(xiàn)了AuthenticationSuccessHandler, AuthenticationFailureHandler
*用于處理登錄成功或者失敗后,跳轉(zhuǎn)的界面
*/
AuthenticationResultHandler handler = new AuthenticationResultHandler();
handler.setForward(forward);
handler.setLoginFailurePage(failurePage);
handler.setLoginSuccessPage(successPage);
handler.setCertLoginUrl(certLoginUrl);
//設(shè)置父類中的處理器
this.setAuthenticationSuccessHandler(handler);
this.setAuthenticationFailureHandler(handler);
}
}
最后為spring-security配置文件中的配置,需要添加authentication-provider的引用,和filter的配置
<security:authentication-manager alias="authenticationManager">
<!-- 注意,這里僅僅是系統(tǒng)默認(rèn)的認(rèn)證機(jī)制,請(qǐng)?jiān)谡较到y(tǒng)中明確知道其功能再使用 -->
<security:authentication-provider ref="acocunt_defaultAnthentiactionProvider"/>
<security:authentication-provider ref="registrationService"/>
<security:authentication-provider ref="enrollmentService"/>
<security:authentication-provider ref="userService"/>
</security:authentication-manager>
<bean id="passcard_filter" class="cn.edu.jszg.cert.security.PasscardAuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="useVerifyCode" value="true"/>
<property name="failurePage" value="/portal/home/auth/"></property>
<property name="config" ref="webAppConfig"/>
<property name="userLogService" ref="userLogService" />
<property name="certLoginUrl" value="${cert.login.url}"/>
</bean>
還要在http中添加<security:custom-filter ref="passcard_filter" after="SECURITY_CONTEXT_FILTER"/>
到此這篇關(guān)于詳解spring security四種實(shí)現(xiàn)方式的文章就介紹到這了,更多相關(guān)spring security 實(shí)現(xiàn)方式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
五分鐘帶你學(xué)會(huì)用java解析json字符串
這篇文章主要給大家介紹了關(guān)于用java解析json字符串的相關(guān)資料,JSON是一種輕量級(jí)的、基于文本的、與語(yǔ)言無(wú)關(guān)的數(shù)據(jù)交換格式,易于人和機(jī)器讀寫,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-07-07
BeanUtils.copyProperties在拷貝屬性時(shí)忽略空值的操作
這篇文章主要介紹了BeanUtils.copyProperties在拷貝屬性時(shí)忽略空值的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
java基礎(chǔ)理論Stream的Filter與謂詞邏輯
這篇文章主要為大家介紹了java基礎(chǔ)理論Stream的Filter與謂詞邏輯,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03
SpringBoot啟動(dòng)并初始化執(zhí)行sql腳本問題
這篇文章主要介紹了SpringBoot啟動(dòng)并初始化執(zhí)行sql腳本問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01
SpringBoot靜態(tài)資源CSS等修改后再運(yùn)行無(wú)效的解決
這篇文章主要介紹了SpringBoot靜態(tài)資源CSS等修改后再運(yùn)行無(wú)效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12
Java8新特性之Collectors.joining()實(shí)例詳解
在項(xiàng)目中我們常常要對(duì)list集合的數(shù)據(jù)做一些字符串拼接/處理等相關(guān)操作,下面這篇文章主要給大家介紹了關(guān)于Java8新特性之Collectors.joining()的相關(guān)資料,需要的朋友可以參考下2023-01-01
windows定時(shí)器配置執(zhí)行java jar文件的方法詳解
這篇文章主要給大家介紹了關(guān)于windows定時(shí)器配置執(zhí)行java jar文件的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11

