思路
表hr 用户表
表hr_role
表role 角色
表menu 菜单
表menu_role
登录成功获取一个id 通过id获取角色 通过角色 知道可以操作哪些菜单
/** 是ant风格的路径匹配符 /** 匹配0或者更多的目录
/employee/basic/**
用户从前台发送http请求->需要通过id获取角色 查看用户是否有权限访问该接口,在有权访问的url中去匹配(拿到http请求后分析地址和url中的哪一个地址是相匹配的)
一级菜单不设置角色
实现
第一步根据用户的请求地址来分析需要的请求角色
package com.akk.vhr.config;
import com.akk.vhr.model.Menu;
import com.akk.vhr.model.Role;
import com.akk.vhr.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
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.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import java.util.Collection;
import java.util.List;
// 该类的功能是启动权限访问,根据用户传来的请求地址,分析出请求需要的角色1
@Component
public class MyFilter implements FilterInvocationSecurityMetadataSource {
@Autowired
MenuService menuService;
AntPathMatcher antPathMatcher = new AntPathMatcher();
// Collection当前请求所需要的角色 Object是FilterInvocation对象
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
// 这里就是获取到了请求的地址
String requestUrl = ((FilterInvocation) object).getRequestUrl();
// 然后就是去数据库去匹配对应的角色 先做查询
List<Menu> allMenusWithRole = menuService.getAllMenusWithRole();
// 一条一条的遍历比较
for (Menu menu : allMenusWithRole) {
// 如果传进来的url在查询的数据中进行比较 当前调符合传进来的url 那么返回这个角色
if (antPathMatcher.match(menu.getUrl(), requestUrl)) {
//获取角色
List<Role> roles = menu.getRoles();
String[] strings = new String[roles.size()];
for (int i = 0; i < roles.size(); i++) {
strings[i] = roles.get(i).getName();
}
// 由于要返回Collection<>类型 所以使用SecurityConfig.createList 他的参数是数组 所以进行了上面的循环遍历
return SecurityConfig.createList(strings);
}
}
// 没有匹配上的 登录之后访问
return SecurityConfig.createList("ROLE_LOGIN");
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
第二步 判断当前用户是否具备这第一步中返回角色,具备就过,不具备就不能过
package com.akk.vhr.config;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import java.util.Collection;
@Component
public class MyDecisionManager implements AccessDecisionManager {
// authentication 用户信息 object请求对象 configAttributes第一步的返回值,即用户想要的角色
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
for (ConfigAttribute configAttribute : configAttributes) {
String needRole = configAttribute.getAttribute();
// 登录即可访问 匿名用户没有角色
if ("ROLE_LOGIN".equals(needRole)) {
// 判断是不是你用用户的实例 如果是就是没登录 抛出异常
if (authentication instanceof AnonymousAuthenticationToken) {
throw new AccessDeniedException("尚未登录,请登录");
}else{
return;
}
}
//不是Role_Login 就和用户里的比较
//获取当前登录用户的角色
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for (GrantedAuthority authority : authorities) {
if (authority.getAuthority().equals(needRole)) {
return;
}
}
}
throw new AccessDeniedException("权限不足,请联系管理员");
}
@Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
第三步 讲第一第二 引入SecurityConfig中
package com.akk.vhr.config;
import com.akk.vhr.model.Hr;
import com.akk.vhr.model.RespBean;
import com.akk.vhr.service.HrService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
HrService hrService;
@Autowired
CustomFilterInvocationSecurityMetadataSource myFilter;
@Autowired
CustomUrlDecisionManager myDecisionManager;
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(hrService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// .anyRequest().authenticated()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setAccessDecisionManager(myDecisionManager);
o.setSecurityMetadataSource(myFilter);
return o;
}
})
.and()
.formLogin()
.usernameParameter("username")
.passwordParameter("password")
.loginProcessingUrl("/doLogin")
.loginPage("/login")
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
Hr principal = (Hr) authentication.getPrincipal();
principal.setPassword(null);
RespBean ok = RespBean.ok("登录成功", principal);
String s = new ObjectMapper().writeValueAsString(ok);
out.write(s);
out.flush();
out.close();
}
})
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
RespBean error = RespBean.error("登陆失败");
if (e instanceof LockedException){
error.setMsg("账户被锁定请联系管理员");
}else if (e instanceof CredentialsExpiredException){
error.setMsg("密码过期请联系管理员");
}else if(e instanceof AccountExpiredException){
error.setMsg("账户过期请联系管理员");
}else if(e instanceof DisabledException){
error.setMsg("账户被禁用,请联系管理员");
}else if(e instanceof BadCredentialsException){
error.setMsg("用户名或密码输入错误,请联系管理员");
}
out.write(new ObjectMapper().writeValueAsString(error));
out.flush();
out.close();
}
})
.permitAll()
.and()
.logout()
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
out.write(new ObjectMapper().writeValueAsString(RespBean.ok("注销成功")));
out.flush();
out.close();
}
})
.permitAll()
.and()
.csrf().disable();
}
}
第四步 给当前角色赋予对应的权限
实体中
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>(roles.size());
for (Role role : roles) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
}
private List<Role> roles; 的赋值是在以下部分进行
@Service
public class HrService implements UserDetailsService {
@Autowired
HrMapper HrMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Hr hr = HrMapper.loadUserByUsername(username);
if (hr == null) {
throw new UsernameNotFoundException("用户不存在");
}
hr.setRoles(HrMapper.getHrRolesById(hr.getId()));
return hr;
}
}
SecurityConfig
// 如果想要访问login 不用经过Security拦截
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/login");
}