1、后台Springboot-Security安全框架
在搭建后台管理框架的时候使用的是Security框架,该框架的执行过程是这样的。
用户类创建
首先要创建一个User类,该类要实现UserDetails接口,在该接口中要实现几个方法,最主要的方法是
/** * 收集用户的角色信息,把角色信息放入到集合中 * @return */ @Override public Collection<? extends GrantedAuthority> getAuthorities() { ArrayList<GrantedAuthority> authorities = new ArrayList<>(); for (Role role : roles) { authorities.add(new SimpleGrantedAuthority(role.getName())); } return authorities; }
该方法主要是收集用户的角色信息。
Serivice中
登录时候通过用户名去数据库中查询,如果该用户不存在则抛出一个UsernameNotFoundException,如果用户存在的话直接返回用户;
1-方式是这样的,该方法主要是对用户的用户名和密码进行校验
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(hrSevice).passwordEncoder(new BCryptPasswordEncoder()); }
2-方式是这样的,该方法主要是放行相应的接口,但是不能放行login接口。如果login是登录的url的话。
@Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("login_p","/static/**"); }
3-方式是这样的
@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(urlFilterConfig); o.setAccessDecisionManager(webAccessDecisionManager); return o; } }) .and() .formLogin().loginPage("/login_p").loginProcessingUrl("/login") .usernameParameter("username").passwordParameter("password") .successHandler(new AuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException { resp.setContentType("application/json;charset=utf-8"); ObjectMapper om = new ObjectMapper(); PrintWriter out = resp.getWriter(); out.write( om.writeValueAsString(RespBean.ok("登录成功!", HrUtils.getCurrentHrInfo()))); out.flush(); out.close(); } }) .failureHandler(new AuthenticationFailureHandler() { @Override public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException { resp.setContentType("application/json;charset=utf-8"); ObjectMapper om = new ObjectMapper(); PrintWriter out = resp.getWriter(); out.write( om.writeValueAsString(RespBean.ok("登录失败!",null))); out.flush(); out.close(); } }) .permitAll() .and() .logout() .logoutUrl("/logout") .permitAll() .and() .csrf().disable().exceptionHandling().accessDeniedHandler(accessExceptionHandler); }
这个带颜色的几个部分别是:
1、所有的请求都学要授权,不然的话没法访问
2、处理具体的授权请求
3、配置用户登录时候的基本信息
4、登录成功或失败的回调函数
5、退出登录时候要处理的事情
6、没有访问权限时候的异常处理
4-处理所有浏览器提交的url
@Component public class UrlFilterConfig implements FilterInvocationSecurityMetadataSource { @Autowired MenuService menuService; @Override public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException { String requestUrl = ((FilterInvocation) o).getRequestUrl(); //拦截所有的url AntPathMatcher antPathMatcher = new AntPathMatcher(); //匹配url使用的 if (requestUrl.equals("/login_p")){ //如果url是以login_p开始的则不需要拦截,直接放行 return null; //如果是登录页不需要认证 } List<Menu> menus = menuService.getAllMenus(); //查询菜单表中所有的内容,并且把 菜单中的每一个项目都和角色对应上关系,注意查询出来的结果也是倒着排列的哦 for (Menu menu : menus) { //如果请求的url是menu中存在的 if (antPathMatcher.match(menu.getUrl(), requestUrl) && menu.getRoles().size()>0){ //如果请求的url在菜单中有并且菜单中的url也有相应的角色 List<Role> roles = menu.getRoles(); //取出该url所具有的角色 int size = roles.size(); String[] values = new String[size]; for (int i = 0; i < roles.size(); i++) { //收集该url所应该具有的角色 values[i] = roles.get(i).getName(); } return SecurityConfig.createList(values); //返回该url应该所具有的角色给下一级 } } //如果请求的url不是数据库中存在的 return SecurityConfig.createList("ROLE_LOGIN"); //如果请求的url在菜单中没有,返回给一个ROLE_LOGIN信息给下一级 } @Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } @Override public boolean supports(Class<?> aClass) { return SecurityConfig.class.isAssignableFrom(aClass); } }
5-是真正处理权限的类
@Component public class WebAccessDecisionManager implements AccessDecisionManager { @Override public void decide(Authentication auth, Object o, Collection<ConfigAttribute> cas) throws AccessDeniedException, InsufficientAuthenticationException { Iterator<ConfigAttribute> iterator = cas.iterator(); //迭代一下,为了能进行下一步 while (cas.iterator().hasNext()){ ConfigAttribute ca = iterator.next(); String needRole = ca.getAttribute(); //访问的路径存在,但是没有登录 if ("ROLE_LOGIN".equals(needRole)){ if (auth instanceof AnonymousAuthenticationToken){ throw new BadCredentialsException("未登录"); } return; } //登录了,访问的路径也存在,但是要判断时候又该权限进行访问 Collection<? extends GrantedAuthority> authorities = auth.getAuthorities(); for (GrantedAuthority authority : authorities) { if (authority.getAuthority().equals(needRole)){ //如果具有的角色是之前登录的角色的一种就可以访问 return; } } } throw new AccessDeniedException("访问权限不足"); } @Override public boolean supports(ConfigAttribute configAttribute) { return false; } @Override public boolean supports(Class<?> aClass) { return false; }
至此,项目的整体搭建基本ok。