自己写一个RBAC实现基于spring security

终于看完慕课网的一个实战视频http://coding.imooc.com/class/134.html
下面来做一个简单的使用springsecurity +JWT的rbac实现
首先创建pom项目

  <dependencyManagement>
  <dependencies>
     
            <dependency>
                <groupId>io.spring.platformgroupId>
                <artifactId>platform-bomartifactId>
                <version>Brussels-SR4version>
                <type>pomtype>
                <scope>importscope>
            dependency>

            
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-dependenciesartifactId>
                <version>Dalston.SR2version>
                <type>pomtype>
                <scope>importscope>
            dependency>
        dependencies>
     dependencyManagement>
         <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <version>3.2version>
                <configuration>
                    <source>1.8source>                
                    <target>1.8target>
                    <encoding>UTF-8encoding>
                configuration>
            plugin>
        plugins>
      build>

创建子项目


       <dependencies>
         <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-oauth2artifactId>
        dependency>
         <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-jdbcartifactId>
        dependency>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
        dependency>
                <dependency>
            <groupId>org.springframework.socialgroupId>
            <artifactId>spring-social-configartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.socialgroupId>
            <artifactId>spring-social-coreartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.socialgroupId>
            <artifactId>spring-social-securityartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.socialgroupId>
            <artifactId>spring-social-webartifactId>
        dependency>
        <dependency>
            <groupId>commons-langgroupId>
            <artifactId>commons-langartifactId>
        dependency>
        <dependency>
            <groupId>commons-collectionsgroupId>
            <artifactId>commons-collectionsartifactId>
        dependency>
        <dependency>
            <groupId>commons-beanutilsgroupId>
            <artifactId>commons-beanutilsartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-configuration-processorartifactId>
        dependency>
           <dependency>
            <groupId>io.jsonwebtokengroupId>
            <artifactId>jjwtartifactId>
            <version>0.7.0version>
        dependency>
        dependencies>  

创建一个springboot启动类(略)

创建一个Bean注册的类

@Configuration
public class SecurityBeanConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

创建一个搜索比较用户名密码的类

@Component
public class MyUserDetailsService implements UserDetailsService {
    private static final Logger logger = LoggerFactory.getLogger(MyUserDetailsService.class);

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return new User(username, passwordEncoder.encode("123456"), 
                AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
    }

}

创建一个安全框架的配置类

//@Configuration
public class SecurityConfig extends  WebSecurityConfigurerAdapter{

    @Autowired
    private AuthenticationSuccessHandler authenticationSuccessHandler;
    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;
    @Autowired
    private UserDetailsService userDetailsService;



    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .formLogin()
                .loginPage("/authentication/require")
                .loginProcessingUrl("/authentication/form")
                .successHandler(authenticationSuccessHandler)
                .failureHandler(authenticationFailureHandler)
            .and()
                .authorizeRequests()
                .antMatchers("/authentication/form",
                        "/authentication/require")
                .permitAll()
                .anyRequest()
                .authenticated()
            .and()
            .csrf().disable()
            ;
    }


}

创建一个oauth2的配置类

@Configuration
@EnableResourceServer
@EnableAuthorizationServer//这个注解实现了认证服务器
public class SecurityTokenConfig implements ResourceServerConfigurer,AuthorizationServerConfigurer {
    @Autowired
    private AuthenticationSuccessHandler authenticationSuccessHandler;
    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private TokenStore tokenStore;
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {

    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients
        .inMemory()
        .withClient("liyq")
        .secret("liyqSecret")
        .accessTokenValiditySeconds(7200)//单位是秒
        .authorizedGrantTypes("refresh_token","password")//指定授权模式
        .scopes("all","read","write")//这里指定了,发请求可以不带,或者在集合内
         ;
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
        .tokenStore(tokenStore)
        .authenticationManager(authenticationManager)
        .userDetailsService(userDetailsService);
        TokenEnhancerChain enhancerChain  = new  TokenEnhancerChain();
        List enhancers = new ArrayList();
        enhancers.add(jwtAccessTokenConverter);
        enhancerChain.setTokenEnhancers(enhancers);
        endpoints
            .tokenEnhancer(enhancerChain)
            .accessTokenConverter(jwtAccessTokenConverter);
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {

    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.formLogin()
        .loginPage("/authentication/require")//配置跳转
        .loginProcessingUrl("/authentication/form")//from 表单的url
        .successHandler(authenticationSuccessHandler)
        .failureHandler(authenticationFailureHandler)//密码登录配置
        .and()
        .authorizeRequests()
        .antMatchers("/authentication/require",
                "/authentication/form")
        .permitAll()
        .and()
        .csrf().disable() //关闭csrf防护
        ;
        }
    }

这里最好将ResourceServerConfigurer和WebSecurityConfigurerAdapter的实现类和并一下,
只保留一个public void configure(HttpSecurity http) 方法进行配置。

自定义URL

@RestController
public class AuthenticationController {
    private static final Logger logger = LoggerFactory.getLogger(AuthenticationController.class);
    //把当前请求缓存到session中
    private RequestCache RequestCache = new HttpSessionRequestCache();
    //这个做跳转
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @RequestMapping("/authentication/require")
    public SimpleResponse requireAuthentication(HttpServletRequest request,HttpServletResponse response) throws IOException {
        //从session中拿到引发跳转的请求
        SavedRequest savedRequest = RequestCache.getRequest(request, response);
        if(savedRequest!=null) {
            String target = savedRequest.getRedirectUrl();
            logger.info("引发跳转的请求是:"+target);
            if(StringUtils.endsWithIgnoreCase(target, ".html")) {
                logger.debug("url:"+target);
                redirectStrategy.sendRedirect(request, response, target);
            }
        }
        return new SimpleResponse("访问的服务需要身份认证,请引导用户到登录页");
    }
}

再创建一个JWT的配置类

@Configuration
public class JwtTokenConfig {
    @Bean
    public TokenStore JwtTokeStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
        accessTokenConverter.setSigningKey("liyq");
        return accessTokenConverter;
    }
}

还有一个成功处理器和一个失败处理器

@Component
public class MyFailureHandler implements AuthenticationFailureHandler{

    private static final Logger logger = LoggerFactory.getLogger(MyFailureHandler.class);

    private ObjectMapper objectMapper = new ObjectMapper();


    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException exception) throws IOException, ServletException {
        logger.info("失败");
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(new SimpleResponse(exception.getMessage())));

    }

}
@Component
public class MySucessHandler implements AuthenticationSuccessHandler{

    private static final Logger logger = LoggerFactory.getLogger(MySucessHandler.class);

    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {
        logger.info("成功");
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(authentication));
    }

}

最后是安全框架跨域请求的问题
一个Filter 配置到WebMvcConfigurer中,
和添加到http.addFilterBefore(myFilter, ChannelProcessingFilter.class)
忽略options请求
builder.ignoring().antMatchers(HttpMethod.OPTIONS);

再然后就是写一个rbac的权限判断方法

@Component("rbacService")
public class RbacServiceImpl implements RbacService{

    private AntPathMatcher antPathMatcher = new  AntPathMatcher();

    @Override
    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        boolean hasPermission = false;
        Object principe = authentication.getPrincipal();
        if(principe instanceof UserDetails) {
            String username  = ((UserDetails)principe).getUsername();
            //拿到用户名后可以拿到用户角色和用户所有的权限
            //读取用户所有的url
            Set urls = new HashSet<>();
            for(String url : urls) {
                if(antPathMatcher.match(url, request.getRequestURI())) {
                    hasPermission = true;
                    break;
                }
            }

        }
        return hasPermission;
    }

}

添加配置

config.anyRequest().access("@rbacService.hasPermission(request,authentication)");

你可能感兴趣的:(学习)