1. 程式人生 > >SpringBoot與SpringSecurity整合的初使用

SpringBoot與SpringSecurity整合的初使用

ans manage arr ESS ini oca ngs bcrypt meta

SpringBoot與SpringSecurity整合的初使用

1.背景:

  最近的一個項目中,需要做web頁面的管理後臺,需要對不同角色進行不同的管理,特此研究了一下SpringSecurity的使用。

2.正題:

  1.采用框架:springboot,springsecurity,jpa

  2.采用idea的spring initializr 初始化一個springboot項目,勾選模塊為web,securiy,和jpa,jdbc。

  3.表結構:

     總共有三張表,分別為user,user_authorities,authentication

      user表

      技術分享圖片

      authentication表

      技術分享圖片

      user_authorities表:

      技術分享圖片

   4.代碼開始:

    4.1 首先,創建jpa對應的實體類:

      這個就不貼代碼了,根據上面的表結構創建對應的類,加上jpa對應的註解即可

    4.2 創建Repository類,繼承JpaRepository

    4.3 創建SpringSecurity配置類WebSecurityConfigurerAdapter:

      

@EnableWebSecurity
public class MySecurityConfig extends
WebSecurityConfigurerAdapter { @Autowired private MyUserDetailService userDetailsService; @Autowired private AuthenticationRepository authenticationRepository; @Override protected void configure(HttpSecurity http) throws Exception { //super.configure(http); http.csrf().disable();
//定義請求規則,查詢數據庫,將authentication表中的請求規則查詢配置 List<Authentication> authentications = authenticationRepository.findAll(); http.authorizeRequests() .antMatchers("/").permitAll(); for(Authentication authentication :authentications){ http.authorizeRequests(). antMatchers(authentication.getDescription()) .hasRole(authentication.getName(). substring(authentication.getName().indexOf("ROLE_")+5)); } //開啟自動配置的登錄功能 http.formLogin(); //1./login請求來到登陸頁面 //2.重定向到/login?error 表示登錄失敗 //3. //開啟自動註銷功能 //訪問logout表示註銷,清空session //註銷成功會默認返回/login?logout頁面 //可以通過配置logoutSuccessUrl來配置註銷成功的返回地址 http.logout().logoutSuccessUrl("/"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(authenticationProvider()); } public DaoAuthenticationProvider authenticationProvider(){ DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); authenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder(8)); authenticationProvider.setUserDetailsService(userDetailsService); return authenticationProvider; } }

    4.4 配置 AccessDecisionManager類

    

@Service
public class MyAccessDecisionManager implements AccessDecisionManager {
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        if (configAttributes == null ) {
            throw new AccessDeniedException("對不起,您沒有此權限");
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(sdf.format(new Date())+":\t"+object.toString());
        System.out.println("configAttributes=="+configAttributes);
        System.out.println("authentication=="+authentication.getAuthorities());
        for(ConfigAttribute ca:configAttributes){
            String needRole = ca.getAttribute();
            for(GrantedAuthority userGA:authentication.getAuthorities()) {
                if(needRole.equals(userGA.getAuthority())) {   // ga is user‘s role.
                    return ;
                }
            }
        }
        throw new AccessDeniedException("對不起,您沒有此權限");
    }

    public boolean supports(ConfigAttribute arg0) {
        // TODO Auto-generated method stub
        return true;
    }

    public boolean supports(Class<?> arg0) {
        // TODO Auto-generated method stub
        return true;
    }

}

    4.5 實現一個繼承userDetailService的類,根據業務邏輯實現loadUserByUserName

@Service
public class MyUserDetailService implements UserDetailsService {
    @Autowired
    private UsersRepository userRepository;
    @Autowired
    private UserAuthoritiesRepository userAuthoritiesRepository;
    @Override
    @Transactional
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(s);
        if(user == null){
            throw new UsernameNotFoundException(s);
        }
        List<GrantedAuthority> authorities = new ArrayList<>();
        List<UserAuthorities> userAuthorities = userAuthoritiesRepository.findAllByUser(user);
        for(UserAuthorities userAuthorities1:userAuthorities){
            authorities.add(new SimpleGrantedAuthority(userAuthorities1.getAuthentication().getName()));
        }
        return new MyUserPrincipal(user,authorities);
    }
}

    4.6 實現AbstractSecurityInterceptor,繼承Filter

      

@Service
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
    @Autowired
    private FilterInvocationSecurityMetadataSource securityMetadataSource;
    @Autowired
    public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
        super.setAccessDecisionManager(myAccessDecisionManager);
    }

    public FilterInvocationSecurityMetadataSource getSecurityMetadataSource(){
        return this.securityMetadataSource;
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        FilterInvocation fi = new FilterInvocation(request, response, chain);
        invoke(fi);
    }

    public void invoke(FilterInvocation fi) throws IOException, ServletException {
        //fi裏面有一個被攔截的url
        //裏面調用MyInvocationSecurityMetadataSource的getAttributes(Object object)這個方法獲取fi對應的所有權限
        //再調用MyAccessDecisionManager的decide方法來校驗用戶的權限是否足夠
        InterceptorStatusToken token = super.beforeInvocation(fi);
        try {
            //執行下一個攔截器
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
            super.afterInvocation(token, null);
        }
    }
    @Override
    public Class<?> getSecureObjectClass() {
        return FilterInvocation.class;
    }

    @Override
    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return this.securityMetadataSource;
    }
}

    4.7 實現FilterInvocationSecurityMetadataSource

@Service
public class MyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource {

    private HashMap<String, Collection<ConfigAttribute>> map = null;

    @Autowired
    private AuthenticationRepository authenticationRepository;
    /**
     * 加載權限表中所有權限,這裏不想從數據庫中獲取直接寫在了這
     */
    public void loadResourceDefine() {
        map = new HashMap<>();
        Collection<ConfigAttribute> array;
//        ConfigAttribute cfg, cfg1,cfg2;
        array = new ArrayList<>();
        List<Authentication> authentications = authenticationRepository.findAll();
        for(Authentication authentication:authentications){
            System.out.println(authentication);
            ConfigAttribute cfg = new SecurityConfig(authentication.getName());
            array.add(cfg);
            map.put(authentication.getDescription(),array);
        }
    }
    //此方法是為了判定用戶請求的url 是否在權限表中,如果在權限表中,則返回給 decide 方法,
    // 用來判定用戶是否有此權限。如果不在權限表中則放行。
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        System.out.println("object的類型為:" + object.getClass());
        FilterInvocation filterInvocation = (FilterInvocation) object;
        String url = filterInvocation.getRequestUrl();
        System.out.println("訪問的URL地址為(包括參數):" + url);
        url = filterInvocation.getRequest().getServletPath();
        System.out.println("訪問的URL地址為:" + url);
        if (map == null)
            loadResourceDefine();
        //object 中包含用戶請求的request 信息
        final HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
        AntPathRequestMatcher matcher;
        String resUrl;
        for (Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); ) {
            resUrl = iter.next();
            matcher = new AntPathRequestMatcher(resUrl);
            //matches() 方法用於檢測字符串是否匹配給定的正則表達式
            boolean a = matcher.matches(request);
            if (matcher.matches(request)) {
                Collection<ConfigAttribute> c = map.get(resUrl);
                return map.get(resUrl);
            }
        }
        return null;
//        return collection;
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        //UsernamePasswordAuthenticationToken.class.equals(clazz);
        return FilterInvocation.class.isAssignableFrom(clazz);
        //return true;
    }
}

    

    

SpringBoot與SpringSecurity整合的初使用