SpringBoot与SpringSecurity整合的初使用

狂风中的少年 提交于 2020-04-29 12:56:17

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;
    }
}

 

    

    

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!