方案一:扩展access()的SpEL表达式
.anyRequest().access("@authService.canAcess(request,authentication)")
方案二:自定义AccessDecisionManager
(1)自定义SecurityMetadataSource,实现从数据库加载ConfigAttribute
(2)自定义accessDecisionManager,进行权限校验
(3)使用withObjectPostProcessor,自定义SecurityMetadataSource和accessDecisionManager
方案三:自定义Filter
spring security本来就是由很多的Filter构成的,那么我们可以自定义Filter然后添加到fiterChain中
(1)需要提供认证数据规则数据源数据:通过实现接口FilterInvocationSecurityMetadataSource来进行实现。
(2)自定义accessDecisionManager:进行权限校验
(3)自定义一个拦截器然后把它添加到spring security的fiterChain
这里使用方案一。
一、创建实体类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;
}
二、建立RoleRepository
public interface RoleRepository extends JpaRepository<Role,Long> {
}
三、创建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;
}
}
四、初始化数据
@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);
}
}
五、基于url动态获取权限并匹配
@Service
public class AuthService {
@Autowired
private PermissionService permissionService;
public boolean canAcess(HttpServletRequest request, Authentication authentication) {
boolean b = false;
String url = request.getRequestURI();
/**
* 1、未登录的情况下,需要坐一个判断或者是拦截。
*/Object principal = authentication.getPrincipal();
if(principal == null || "anonymousUser".equals(principal)) {
return b;
}
/**
* 2、匿名的角色ROLE_ANONYMOUS
*/
if(authentication instanceof AnonymousAuthenticationToken) {
// 匿名角色
// check
// return
}
/**
* 3、通过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;
}
/**
* 4、将获取到的权限信息和当前的登陆账号的权限信息进行对比
*/
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;
}
}
六、使用.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();//所有的请求需要在登陆之后才能访问
}
遇到问题: 1、登陆成功后总是现实无权访问,这里先将首页设为所以可见。
.antMatchers("/","/index").permitAll()
2、登陆成功后总是访问/favicon.ico,导致权限无法匹配成功,解决办法是加一个/favicon.ico图片,再在使用的页面调用。还有更好的办法。 https://www.jianshu.com/p/b56e524ba2e8 https://blog.csdn.net/sdjadycsdn/article/details/82621234