方案一:擴展access()的SpEL表達式html
.anyRequest().access("@authService.canAcess(request,authentication)")spring
方案二:自定義AccessDecisionManager數據庫
(1)自定義SecurityMetadataSource,實現從數據庫加載ConfigAttributesession
(2)自定義accessDecisionManager,進行權限校驗ide
(3)使用withObjectPostProcessor,自定義SecurityMetadataSource和accessDecisionManagerfetch
方案三:自定義Filterthis
spring security原本就是由不少的Filter構成的,那麼咱們能夠自定義Filter而後添加到fiterChain中url
(1)須要提供認證數據規則數據源數據:經過實現接口FilterInvocationSecurityMetadataSource來進行實現。.net
(2)自定義accessDecisionManager:進行權限校驗code
(3)自定義一個攔截器而後把它添加到spring security的fiterChain
這裏使用方案一。
1、建立實體類Permission
@Entity public class Permission { @Id @GeneratedValue private long id; // 主鍵 private String name; // 權限名稱 private String description; // 權限名稱 /** * 注意:Permission表的ulr通配符爲兩顆星,好比/user下的全部url,應該寫成/user/** */ private String url; // 受權鏈接 private Long pid; // 父節點 // 角色 - 權限是多對多的關係 @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "RolePermission",joinColumns = {@JoinColumn(name="permission_id")},inverseJoinColumns = {@JoinColumn(name="role_id")}) private List<Role> roles; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; }
2、創建RoleRepository
public interface RoleRepository extends JpaRepository<Role,Long> { }
3、建立service類
public interface PermissionService { Map<String, Collection<ConfigAttribute>> getPermissionMap (); } @Service public class PermissionServiceImpl implements PermissionService { @Autowired private PermissionRepository permissionRepository; private Map<String, Collection<ConfigAttribute>> permissionMap = null; @PostConstruct /** * 從數據庫中獲取全部權限信息,而後進行遍歷,存儲到permissionMap集合中 */ public void initPermissions() { permissionMap = new HashMap<>(); List<Permission> permissions = permissionRepository.findAll(); for (Permission p : permissions) { Collection<ConfigAttribute> collection = new ArrayList<ConfigAttribute>(); for (Role role : p.getRoles()) { ConfigAttribute configAttribute = new SecurityConfig("ROLE_"+role.getName()); collection.add(configAttribute); } permissionMap.put(p.getUrl(),collection); } System.out.println(permissionMap); } @Override public Map<String,Collection<ConfigAttribute>> getPermissionMap (){ if(permissionMap == null || permissionMap.size() == 0){ initPermissions(); } return permissionMap; } }
4、初始化數據
@Service public class DataInit { @Autowired private UserInfoRepository userInfoRepository; @Autowired private PasswordEncoder passwordEncoder; @Autowired private RoleRepository roleRepository; @Autowired private PermissionRepository permissionRepository; @PostConstruct public void dataInit(){ // role List<Role> roles = new ArrayList<>(); Role adminRole = new Role(); adminRole.setName("admin"); adminRole.setDescprtion("管理員"); roleRepository.save(adminRole); roles.add(adminRole); Role userRole = new Role(); userRole.setName("normal"); userRole.setDescprtion("普通用戶"); roleRepository.save(userRole); roles.add(userRole); Permission userPermission = new Permission(); userPermission.setName("普通用戶的url"); userPermission.setDescription("容許普通用戶訪問"); userPermission.setUrl("/hello/helloUser"); userPermission.setRoles(roles); permissionRepository.save(userPermission); Permission adminPermission = new Permission(); adminPermission.setName("管理員的url"); adminPermission.setDescription("容許管理員訪問"); adminPermission.setUrl("/hello/helloAdmin"); roles = new ArrayList<>(); roles.add(adminRole); adminPermission.setRoles(roles); permissionRepository.save(adminPermission); // admin UserInfo admin = new UserInfo(); admin.setUsername("admin"); admin.setPassword(passwordEncoder.encode("123")); admin.setRoles(roles); userInfoRepository.save(admin); // user roles = new ArrayList<>(); roles.add(userRole); UserInfo user = new UserInfo(); user.setUsername("user"); user.setPassword(passwordEncoder.encode("123")); user.setRoles(roles); userInfoRepository.save(user); } }
5、基於url動態獲取權限並匹配
@Service public class AuthService { @Autowired private PermissionService permissionService; public boolean canAcess(HttpServletRequest request, Authentication authentication) { boolean b = false; String url = request.getRequestURI(); /** * 一、未登陸的狀況下,須要坐一個判斷或者是攔截。 */Object principal = authentication.getPrincipal(); if(principal == null || "anonymousUser".equals(principal)) { return b; } /** * 二、匿名的角色ROLE_ANONYMOUS */ if(authentication instanceof AnonymousAuthenticationToken) { // 匿名角色 // check // return } /** * 三、經過requst對象url,獲取到權限信息 */ Map<String, Collection<ConfigAttribute>> map = permissionService.getPermissionMap(); Collection<ConfigAttribute> configAttributes = null; for (Iterator<String> it = map.keySet().iterator();it.hasNext();) { String curUrl = it.next(); AntPathRequestMatcher matcher = new AntPathRequestMatcher(curUrl); if (matcher.matches((request))) { configAttributes = map.get(curUrl); break; } } if(configAttributes == null || configAttributes.size() == 0) { return b; } /** * 四、將獲取到的權限信息和當前的登錄帳號的權限信息進行對比 */ for(Iterator<ConfigAttribute> it = configAttributes.iterator();it.hasNext();){ ConfigAttribute cfa = it.next(); String role = cfa.getAttribute(); for(GrantedAuthority authority : authentication.getAuthorities()){ if(role.equals(authority.getAuthority())){ b = true; break; } } } return b; } }
6、使用.anyRequest().access()
@Override protected void configure (HttpSecurity http) throws Exception{ http.formLogin().loginPage("/login") .and() .authorizeRequests() .antMatchers("/login").permitAll() //容許全部人能夠訪問登錄頁面 .antMatchers("/","/index").permitAll() .antMatchers("/test/**","/test1/**","/favicon.ico").permitAll() .antMatchers("/res/**/*.{js,html}").permitAll() .anyRequest().access("@authService.canAcess(request,authentication)") .and().sessionManagement().maximumSessions(1); //.anyRequest().authenticated();//全部的請求須要在登錄以後才能訪問 }
遇到問題: 一、登錄成功後老是現實無權訪問,這裏先將首頁設爲因此可見。
.antMatchers("/","/index").permitAll()
二、登錄成功後老是訪問/favicon.ico,致使權限沒法匹配成功,解決辦法是加一個/favicon.ico圖片,再在使用的頁面調用。還有更好的辦法。 https://www.jianshu.com/p/b56e524ba2e8 https://blog.csdn.net/sdjadycsdn/article/details/82621234