首先建立4個類ide
流程大體以下:this
一、容器啓動 加載系統資源與權限列表(HashMap) MyInvocationSecurityMetadataSourceService中的loadResourceDefineurl
@Service public class MyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource { @Autowired private PermissionDao permissionDao; private HashMap<String, Collection<ConfigAttribute>> map =null; /** * 加載全部資源與權限的關係 */ public void loadResourceDefine(){ map = new HashMap<>(); Collection<ConfigAttribute> array; ConfigAttribute cfg; List<Permission> permissions = permissionDao.findAll(); for(Permission permission : permissions) { array = new ArrayList<>(); //角色名稱 cfg = new SecurityConfig(permission.getName()); array.add(cfg); map.put(permission.getUrl(), array); } } /** *返回請求資源所須要的權限 */ @Override public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { if(map ==null) loadResourceDefine(); HttpServletRequest request = ((FilterInvocation) object).getHttpRequest(); AntPathRequestMatcher matcher; String resUrl; for(Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); ) { resUrl = iter.next(); matcher = new AntPathRequestMatcher(resUrl); if(matcher.matches(request)) { return map.get(resUrl); } } return null; } @Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } @Override public boolean supports(Class<?> clazz) { return true; } }
二、用戶發出請求spa
三、過濾器攔截 MyFilterSecurityInterceptor中的doFilter()code
@Service public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter { /** * 過濾請求 調用FilterInvocationSecurityMetadataSource 資源和 MyAccessDecisionManager進行驗證 */ @Autowired private FilterInvocationSecurityMetadataSource securityMetadataSource; @Autowired public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) { super.setAccessDecisionManager(myAccessDecisionManager); } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { /** * FilterInvocation 把doFilter傳進來的request、response、chain對象保存起來 */ FilterInvocation fi = new FilterInvocation(request, response, chain); invoke(fi); } public void invoke(FilterInvocation fi) throws IOException, ServletException { //fi裏面有一個被攔截的url //裏面調用MyInvocationSecurityMetadataSource的getAttributes(Object object)這個方法獲取fi對應的全部權限 HashMap中 //再調用MyAccessDecisionManager的decide方法來校驗用戶的權限是否足夠 //object爲FilterInvocation對象 // //一、獲取請求資源的權限 //執行Collection<ConfigAttribute> attributes=securityMetadataSource.getAttributes(fi); //二、是否擁有權限 //執行 this.accessDecisionManager.decide(authenticated,fi,attributes); InterceptorStatusToken token = super.beforeInvocation(fi); /** * InterceptorStatusToken token = super.beforeInvocation(fi);會調用MyAccessDecisionManager中decide方法 * 和MyInvocationSecurityMetadataSource中getAttributes方法 */ try { //執行下一個攔截器 fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } finally { super.afterInvocation(token, null); } } @Override public void destroy() { } @Override public Class<?> getSecureObjectClass() { return FilterInvocation.class; } @Override public SecurityMetadataSource obtainSecurityMetadataSource() { return this.securityMetadataSource; } }
四、取得請求資源所需權限MyInvocationSecurityMetadataSourceService中getAttributes()對象
五、匹配用戶的權限和請求所須要的權限MyAccessDecisionManager中decide()blog
@Service public class MyAccessDecisionManager implements AccessDecisionManager { /** * @param authentication 用戶具備的角色權限,認證的入口 * @param object 當前正在請求的受保護的對象 * @param configAttributes 受保護對象的配置屬性—所具備的權限 * @throws AccessDeniedException * @throws InsufficientAuthenticationException */ @Override public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { //若是訪問資源不須要任何權限則直接經過 if(null== configAttributes || configAttributes.size() <=0) { return; } ConfigAttribute c; String needRole; //遍歷configAttributes看用戶是否有訪問資源的權限 for(Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext(); ) { c = iter.next(); needRole = c.getAttribute(); for(GrantedAuthority ga : authentication.getAuthorities()) { //ga 用戶所被賦予的權限,needRole 訪問相應資源應具備的權限 if(needRole.trim().equals(ga.getAuthority())) { return; } } } // 若是訪問被拒絕,實現將拋出一個AccessDeniedException異常。 throw new AccessDeniedException("no right"); } @Override public boolean supports(ConfigAttribute attribute) { return true; } @Override public boolean supports(Class<?> clazz) { return true; } }
六、登陸token
@Service public class CustomUserService implements UserDetailsService { //自定義UserDetailsService 接口 @Autowired UserDao userDao; @Autowired PermissionDao permissionDao; /** *點擊登陸跳轉 保存用戶權限 * */ public UserDetails loadUserByUsername(String username) { SysUser user = userDao.findByUserName(username); if (user != null) { List<Permission> permissions = permissionDao.findByAdminUserId(user.getId()); List<GrantedAuthority> grantedAuthorities = new ArrayList <>(); for (Permission permission : permissions) { if (permission != null && permission.getName()!=null) { GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permission.getName()); grantedAuthorities.add(grantedAuthority); } } return new User(user.getUsername(), user.getPassword(), grantedAuthorities); } else { throw new UsernameNotFoundException("admin: " + username + " do not exist!"); } } }
七、驗證並受權接口