SpringSecurity曾经在十年前很是火热,只要是作权限系统,当时几乎非用它不可,记得是在XML文件里一堆的配置。曾几什么时候,Shiro冒了出来,以其简洁和轻量的风格慢慢地捕获了众多码农的心,今后SpringSecurity彷佛成了历史文物。 html
但事物老是在发展变化的,这两年随着 SpringBoot的兴起,因为SpringSecurity与SpringBoot都是Spring家族成员,在整合上具有自然优点,且SpringSecurity功能相对Shiro更加完善,对OAUTH认证支持得比较好,因此在微服务架构中又获得了普遍应用。git
在SpringBoot下使用SpringSecurity很是的简单,只要保证在项目的classpath下引入了相应的jar包就能够了。启动类上也无需添加什么。github
<!--SpringSecurity--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
public class Hr implements UserDetails { private Long id; private String name; private String phone; private String telephone; private String address; private boolean enabled; private String username; private String password; private String remark; private List<Role> roles; private String userface; //getter/setter省略 }
@Service @Transactional public class HrService implements UserDetailsService { @Autowired HrMapper hrMapper; @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { Hr hr = hrMapper.loadUserByUsername(s); if (hr == null) { throw new UsernameNotFoundException("用户名不对"); } return hr; } }
@Component public class UrlFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource { @Autowired MenuService menuService; AntPathMatcher antPathMatcher = new AntPathMatcher(); @Override public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException { //获取请求地址 String requestUrl = ((FilterInvocation) o).getRequestUrl(); if ("/login_p".equals(requestUrl)) { return null; } List<Menu> allMenu = menuService.getAllMenu(); for (Menu menu : allMenu) { if (antPathMatcher.match(menu.getUrl(), requestUrl)&&menu.getRoles().size()>0) { List<Role> roles = menu.getRoles(); int size = roles.size(); String[] values = new String[size]; for (int i = 0; i < size; i++) { values[i] = roles.get(i).getName(); } return SecurityConfig.createList(values); } } //没有匹配上的资源,都是登陆访问 return SecurityConfig.createList("ROLE_LOGIN"); } @Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } @Override public boolean supports(Class<?> aClass) { return FilterInvocation.class.isAssignableFrom(aClass); } }
@Component public class UrlAccessDecisionManager implements AccessDecisionManager { @Override public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, AuthenticationException { Iterator<ConfigAttribute> iterator = collection.iterator(); while (iterator.hasNext()) { ConfigAttribute ca = iterator.next(); //当前请求须要的权限 String needRole = ca.getAttribute(); if ("ROLE_LOGIN".equals(needRole)) { if (authentication instanceof AnonymousAuthenticationToken) { throw new BadCredentialsException("未登陆"); } else return; } //当前用户所具备的权限 Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities(); for (GrantedAuthority authority : authorities) { if (authority.getAuthority().equals(needRole)) { return; } } } throw new AccessDeniedException("权限不足!"); } @Override public boolean supports(ConfigAttribute configAttribute) { return true; } @Override public boolean supports(Class<?> aClass) { return true; } }
@Component public class AuthenticationAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest httpServletRequest, HttpServletResponse resp, AccessDeniedException e) throws IOException, ServletException { resp.setStatus(HttpServletResponse.SC_FORBIDDEN); resp.setCharacterEncoding("UTF-8"); PrintWriter out = resp.getWriter(); out.write("{\"status\":\"error\",\"msg\":\"权限不足,请联系管理员!\"}"); out.flush(); out.close(); } }
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired HrService hrService; @Autowired UrlFilterInvocationSecurityMetadataSource urlFilterInvocationSecurityMetadataSource; @Autowired UrlAccessDecisionManager urlAccessDecisionManager; @Autowired AuthenticationAccessDeniedHandler authenticationAccessDeniedHandler; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(hrService); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/index.html", "/static/**"); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() { @Override public <O extends FilterSecurityInterceptor> O postProcess(O o) { o.setSecurityMetadataSource(urlFilterInvocationSecurityMetadataSource); o.setAccessDecisionManager(urlAccessDecisionManager); return o; } }).and().formLogin().loginPage("/login_p").loginProcessingUrl("/login").usernameParameter("username").passwordParameter("password").permitAll().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(); StringBuffer sb = new StringBuffer(); sb.append("{\"status\":\"error\",\"msg\":\""); if (e instanceof UsernameNotFoundException || e instanceof BadCredentialsException) { sb.append("用户名或密码输入错误,登陆失败!"); } else if (e instanceof DisabledException) { sb.append("帐户被禁用,登陆失败,请联系管理员!"); } else { sb.append("登陆失败!"); } sb.append("\"}"); out.write(sb.toString()); out.flush(); out.close(); } }).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(); ObjectMapper objectMapper = new ObjectMapper(); String s = "{\"status\":\"success\",\"msg\":" + objectMapper.writeValueAsString(HrUtils.getCurrentHr()) + "}"; out.write(s); out.flush(); out.close(); } }).and().logout().permitAll().and().csrf().disable().exceptionHandling().accessDeniedHandler(authenticationAccessDeniedHandler); } }
github源码参见:https://github.com/liming0517/SpringSecurityDemo
其余参考:https://github.com/lenve/vhr/wiki/3.%E5%8A%A8%E6%80%81%E5%A4%84%E7%90%86%E8%A7%92%E8%89%B2%E5%92%8C%E8%B5%84%E6%BA%90%E7%9A%84%E5%85%B3%E7%B3%BB#%E8%87%AA%E5%AE%9A%E4%B9%89filterinvocationsecuritymetadatasource