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