springboot2.0整合springsecurity先後端分離進行自定義權限控制

  在閱讀本文以前能夠先看看springsecurity的基本執行流程,下面我展現一些核心配置文件,後面給出完整的整合代碼到git上面,有興趣的小夥伴能夠下載進行研究git

  使用maven工程構建項目,首先須要引入最核心的依賴,github

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

因爲這裏咱們整合的項目進行了先後端分離,因此咱們首先須要自定義登陸成功和失敗,登出成功的自定義處理類web

其實就是實現不一樣的handler便可:1.首先咱們來看登陸成功的處理類spring

/**
 * 處理登陸驗證成功的類
 * @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.登陸驗證失敗的類json

/**
 * 處理登陸驗證失敗的類
 * @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類,下面是配置文件的詳情springboot

/**
 * 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代碼以下app

/**
 * 實現本身的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)cors

完成登陸邏輯還須要咱們實現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上面下載下來研究,不懂得能夠交流,代碼有什麼不妥的地方也望你們互相指教

github示例代碼

相關文章
相關標籤/搜索