項(xiàng)目簡(jiǎn)介
原創(chuàng) libertinus 出處Spring Security 自定義 登陸 權(quán)限驗(yàn)證
基于Spring Cloud 的項(xiàng)目,Spring Cloud是在Spring Boot上搭建的所以按照Spring Boot的方式來(lái)寫(xiě)
Spring Security 配置
繼承 WebSecurityConfigurerAdapter
,重寫(xiě)configure(HttpSecurity http)
配置相關(guān)權(quán)限以及重寫(xiě)攔截器
http.authorizeRequests()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated().and()
//證書(shū) 認(rèn)證 自動(dòng)登陸
.addFilterBefore(authTokenFilter, UsernamePasswordAuthenticationFilter.class)
//登陸以及權(quán)限控制Filter
......
;
自定義UsernamePasswordAuthenticationFilter
自定義 UsernamePasswordAuthenticationFilter 實(shí)現(xiàn)自動(dòng)登陸
創(chuàng)建Authentication 模擬登陸
Authentication authentication = new UsernamePasswordAuthenticationToken(auth, token);
SecurityContextHolder.getContext().setAuthentication(authentication);;
自定義FilterSecurityInterceptor
Spring Security 是通過(guò)這個(gè)過(guò)濾器來(lái)實(shí)現(xiàn) Http資源安全過(guò)濾的。
獲取資源權(quán)限
FilterSecurityInterceptor繼承自 AbstractSecurityInterceptor ,源碼中的其中beforeInvocation方法的一段代碼是:
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
.getAttributes(object);
這個(gè)方法是來(lái)獲取資源權(quán)限 ,可以重寫(xiě)SecurityMetadataSource obtainSecurityMetadataSource(){}
方法來(lái)實(shí)現(xiàn),傳入一個(gè)FilterInvocation對(duì)象,返回一個(gè)Collection<ConfigAttribute>對(duì)象。
這個(gè)對(duì)象中可以獲取到request, response等內(nèi)置對(duì)象,可以通過(guò)一下代碼來(lái)匹配
RequestMatcher requestMatcher = new AntPathRequestMatcher("/manager/**");
if(requestMatcher.matches(request)){
return RESOURCE
}
ConfigAttribute 可以通過(guò)new SecurityConfig((String)input) 來(lái)創(chuàng)建
編寫(xiě)認(rèn)證提供者
重寫(xiě) AuthenticationManager 實(shí)現(xiàn),用戶登陸可以放這里面
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
用來(lái)生成Authentication, 原始的夠用的話直接注入設(shè)置就好。
用戶是否有獲取資源權(quán)限
AbstructSecurityIntercepter 中的一下方法來(lái)判斷用戶權(quán)限是否可以擁有該資源
this.accessDecisionManager.decide(authenticated, object, attributes);
為了達(dá)到自定義控制的目的,我們需要實(shí)現(xiàn)AccessDecisionManager接口,來(lái)重寫(xiě)這個(gè)方法,如果判斷不通過(guò) decide方法可以拋出AccessDeniedException,來(lái)阻止用戶訪問(wèn)
/**
* 判斷用戶是否有訪問(wèn)資源權(quán)限
* @param authentication 用戶Auth
* @param object FilterInvocation對(duì)象
* @param configAttributes 資源所需權(quán)限
* @throws AccessDeniedException 無(wú)權(quán)限Exception
* @throws InsufficientAuthenticationException
*/
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
if(access){
//允許通過(guò)
return;
}
//不允許角色訪問(wèn)
throw new AccessDeniedException("NO ALLOW");
}
JAVA 源碼片
WebSecurityConfig
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthTokenFilter authTokenFilter;
@Autowired
private ApiPermissionSecurityFilter securityFilter;
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated().and()
//證書(shū) 認(rèn)證 自動(dòng)登陸
.addFilterBefore(authTokenFilter, UsernamePasswordAuthenticationFilter.class)
//登陸以及權(quán)限控制Filter
.addFilterBefore(securityFilter, FilterSecurityInterceptor.class)
.csrf().disable()
//基于Token 不需要Session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
;
}
}
AuthTokenFilter (自定義UsernamePasswordAuthenticationFilter)
@Component
public class AuthTokenFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String auth = request.getHeader("Authorization");
//用戶登陸,暫不設(shè)置權(quán)限
Token token = new Token(auth, null);
Authentication authentication = new UsernamePasswordAuthenticationToken(auth, token);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}
ApiPermissionSecurityFilter
@Component
public class ApiPermissionSecurityFilter extends AbstractSecurityInterceptor implements Filter {
@Autowired
private ApiInvocationSecurityMetadataSourceService apiInvocationSecurityMetadataSourceService;
@Autowired
private ApiAccessDecisionManager apiAccessDecisionManager;
@Autowired
private AuthenticationManager authenticationManager;
@PostConstruct
public void init(){
super.setAuthenticationManager(authenticationManager);
super.setAccessDecisionManager(apiAccessDecisionManager);
}
public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException{
FilterInvocation fi = new FilterInvocation( request, response, chain );
invoke(fi);
}
public Class<? extends Object> getSecureObjectClass(){
return FilterInvocation.class;
}
public void invoke( FilterInvocation fi ) throws IOException, ServletException{
InterceptorStatusToken token = super.beforeInvocation(fi);
try{
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
}finally{
super.afterInvocation(token, null);
}
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource(){
return this.apiInvocationSecurityMetadataSourceService;
}
public void destroy(){
}
public void init( FilterConfig filterconfig ) throws ServletException{
}
}
ApiInvocationSecurityMetadataSourceService
/**
* 資源-權(quán)限控制對(duì)象
* Created by liang on 2017/3/17.
*/
@Component
public class ApiInvocationSecurityMetadataSourceService implements
FilterInvocationSecurityMetadataSource {
//緩存 英文名-權(quán)限
private static LoadingCache<String, Collection<ConfigAttribute>> permitMap = null;
//緩存 英文名-ODCINFO信息對(duì)象
private static LoadingCache<String, OdcInfo> odcInfoMap = null;
@PostConstruct
private void init() {
//資源啟動(dòng)時(shí)初始化 資源和角色權(quán)限
//緩存 英文名-權(quán)限 初始化
//緩存 英文名-ODCINFO
}
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
FilterInvocation filterInvocation = (FilterInvocation) object;
//TODO 干你想干事情,下面是獲取路徑所具有的資源
return permitMap.get(getHttpRequest().getRequestURI());
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return new ArrayList<ConfigAttribute>();
}
@Override
public boolean supports(Class<?> aClass) {
//很重要,不然不起作用
return true;
}
}
ApiAccessDecisionManager
@Component
public class ApiAccessDecisionManager implements AccessDecisionManager {
/**
* 判斷用戶是否有訪問(wèn)資源權(quán)限
* @param authentication 用戶Auth
* @param object FilterInvocation對(duì)象
* @param configAttributes 資源所需權(quán)限
* @throws AccessDeniedException 無(wú)權(quán)限Exception
*/
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException {
if(access){
//允許通過(guò)
return;
}
//不允許角色訪問(wèn)
throw new AccessDeniedException("NO ALLOW");
}
public boolean supports( ConfigAttribute attribute ){
return true;
}
public boolean supports(Class<?> clazz){
return true;
}
}