springboot2.0整合springsecurity前后端分离进行自定义权限控制
在阅读本文之前可以先看看springsecurity的基本执行流程,下面我展示一些核心配置文件,后面给出完整的整合代码到git上面,有兴趣的小伙伴可以下载进行研究
使用maven工程构建项目,首先需要引入最核心的依赖,
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
由于这里我们整合的项目进行了前后端分离,所以我们首先需要自定义登录成功和失败,登出成功的自定义处理类
其实就是实现不同的handler即可:1.首先我们来看登录成功的处理类
/** * 处理登录验证成功的类 * @author zhoukebo * @date 2018/9/4 */ @Component public class FuryAuthSuccessHandler implements AuthenticationSuccessHandler { /**Json转化工具*/ @Autowired private ObjectMapper objectMapper; @Override public void onAuthenticationSuccess(HttpServletRequest request,HttpServletResponse response, Authentication authentication) throws IOException{ SysUser userDetails = (SysUser)authentication.getPrincipal(); System.out.println("管理员 " + userDetails.getUsername() + " 登录"); Map<String,String> map=new HashMap<>(2); map.put("code", "200"); map.put("msg", "登录成功"); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(map)); } }
2.登录验证失败的类
/** * 处理登录验证失败的类 * @author zhoukebo * @date 2018/9/4 */ @Component public class FuryAuthFailureHandler implements AuthenticationFailureHandler { @Autowired private ObjectMapper objectMapper; @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { System.out.println("登录验证失败"); Map<String,String> map=new HashMap<>(2); map.put("code", "10001"); map.put("msg", exception.getMessage()); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(map)); } }
3.自定义处理注销成功的类
/** * 处理注销成功 * @author zhoukebo * @date 2018/9/4 */ @Component public class MyLogoutSuccessHandler implements LogoutSuccessHandler { /**Json转化工具*/ @Autowired private ObjectMapper objectMapper; @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException{ Map<String,String> map=new HashMap<>(2); map.put("code", "200"); map.put("msg", "登出成功"); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(map)); } }
4.自定义没有权限的处理类
/** * 处理没有权限的类 * @author zhoukebo * @date 2018/9/5 */ @Component public class RestAuthAccessDeniedHandler implements AccessDeniedHandler { @Autowired private ObjectMapper objectMapper; @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException { Map<String,String> map=new HashMap<>(2); map.put("code", "403"); map.put("msg", e.getMessage()); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(map)); } }
然后对springsecurity进行详细的配置需要继承WebSecurityConfigurerAdapter类,下面是配置文件的详情
/** * spring Security配置安全控制中心 * * @author zhoukb */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { /** * 依赖注入自定义的登录成功处理器 */ @Autowired private FuryAuthSuccessHandler furyAuthSuccessHandler; /** * 依赖注入自定义的登录失败处理器 */ @Autowired private FuryAuthFailureHandler furyAuthFailureHandler; /** * 依赖注入自定义的注销成功的处理器 */ @Autowired private MyLogoutSuccessHandler myLogoutSuccessHandler; /** * 注册没有权限的处理器 */ @Autowired private RestAuthAccessDeniedHandler restAuthAccessDeniedHandler; /***注入自定义的CustomPermissionEvaluator*/ @Bean public DefaultWebSecurityExpressionHandler webSecurityExpressionHandler() { DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler(); handler.setPermissionEvaluator(new CustomPermissionEvaluator()); return handler; } /***注入我们自己的登录逻辑验证器AuthenticationProvider*/ @Autowired private AuthenticationProvider authenticationProvider; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //这里可启用我们自己的登陆验证逻辑 auth.authenticationProvider(authenticationProvider); } /** * 配置spring security的控制逻辑 */ @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() //"/login"不进行权限验证 .antMatchers("/login").permitAll() .antMatchers("/favicon.ico").permitAll() .anyRequest().authenticated() //其他的需要登陆后才能访问 .and() .formLogin() //loginProcessingUrl用于指定前后端分离的时候调用后台登录接口的名称 .loginProcessingUrl("/login") //配置登录成功的自定义处理类 .successHandler(furyAuthSuccessHandler) //配置登录失败的自定义处理类 .failureHandler(furyAuthFailureHandler) .and() //loginProcessingUrl用于指定前后端分离的时候调用后台注销接口的名称 .logout().logoutUrl("/logout") .logoutSuccessHandler(myLogoutSuccessHandler) .and() //配置没有权限的自定义处理类 .exceptionHandling().accessDeniedHandler(restAuthAccessDeniedHandler) .and() .cors()//新加入 .and() .csrf().disable();// 取消跨站请求伪造防护 } }
上面我们配置了自定义的登录逻辑的验证MyAuthenticationProvider,和自定义的权限验证CustomPermissionEvaluator代码如下
/** * 实现自己的AuthenticationProvider类,用来自定义用户校验机制 * @author zhoukebo * @date 2018/9/5 */ @Component public class MyAuthenticationProvider implements AuthenticationProvider { @Autowired private CustomerDetailService customerDetailService; @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { // 获取表单输入中返回的用户名; String userName = (String) authentication.getPrincipal(); // 获取表单中输入的密码; String password = (String) authentication.getCredentials(); // 这里调用我们的自己写的获取用户的方法; UserDetails userInfo = customerDetailService.loadUserByUsername(userName); if (userInfo == null) { throw new BadCredentialsException("用户名不存在"); } // 这里我们还要判断密码是否正确,这里我们的密码使用BCryptPasswordEncoder进行加密的 if (!new BCryptPasswordEncoder().matches(password, userInfo.getPassword())) { throw new BadCredentialsException("密码不正确"); } // 这里还可以加一些其他信息的判断,比如用户账号已停用等判断。 Collection<? extends GrantedAuthority> authorities = userInfo.getAuthorities(); // 构建返回的用户登录成功的token return new UsernamePasswordAuthenticationToken(userInfo, password, authorities); } @Override public boolean supports(Class<?> authentication) { // 这里直接改成retrun true;表示是支持这个执行 return true; } }
/** * 我们需要自定义对hasPermission()方法的处理, * 就需要自定义PermissionEvaluator,创建类CustomPermissionEvaluator,实现PermissionEvaluator接口。 * @author zhoukebo * @date 2018/9/5 */ @Component public class CustomPermissionEvaluator implements PermissionEvaluator { /** * 自定义验证方法 * @param authentication 登录的时候存储的用户信息 * @param targetDomainObject @PreAuthorize("hasPermission('/hello/**','r')") 中hasPermission的第一个参数 * @param permission @PreAuthorize("hasPermission('/hello/**','r')") 中hasPermission的第二个参数 * @return */ @Override public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) { // 获得loadUserByUsername()方法的结果 SysUser user = (SysUser)authentication.getPrincipal(); // 获得loadUserByUsername()中注入的权限 Collection<? extends GrantedAuthority> authorities = user.getAuthorities(); // 遍历用户权限进行判定 for(GrantedAuthority authority : authorities) { UrlGrantedAuthority urlGrantedAuthority = (UrlGrantedAuthority) authority; String permissionUrl = urlGrantedAuthority.getPermissionUrl(); // 如果访问的Url和权限用户符合的话,返回true if(targetDomainObject.equals(permissionUrl)) { return true; } } return false; } @Override public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) { return false; } }
注意:上面的自定义权限要生效还需要在WebSecurityConfig上面加上注解@EnableGlobalMethodSecurity(prePostEnabled = true)
完成登录逻辑还需要我们实现UserDetailsService接口,以便系统能够根据用户名去获取用户的信息,里面还可加上自己的逻辑
/**
* 需要自定义UserDetailsService实现spring security的UserDetailsService接口
* @author zhoukebo
* @date 2018/9/4
*/
@Service
public class CustomerDetailService implements UserDetailsService {
@Autowired
SysUserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("用户名不存在");
}
List<SysRole> roles = user.getRoles();
//将所有的角色对应的资源权限全部放入user对应的grantedAuthority集合中
for (SysRole role : roles) {
List<SysResource> resources = role.getResources();
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
for (SysResource resource : resources) {
if (resource != null && resource.getResourceName()!=null) {
GrantedAuthority grantedAuthority = new UrlGrantedAuthority(resource.getMethodPath(),resource.getResourceName());
grantedAuthorities.add(grantedAuthority);
}
}
user.setGrantedAuthority(grantedAuthorities);
}
System.out.println("s:" + username);
return user;
}
}
以上就完成了springboot和springsecurity的整合工作,demo中包含两种自定义权限验证,有兴趣的小伙伴可以自行在github上面下载下来研究,不懂得可以交流,代码有什么不妥的地方也望大家互相指教
转自:https://www.cnblogs.com/zhoukebo/p/9674361.html