Spring Security的介绍和简单使用

前言

Spring Security作为基于Spring的安全框架,具有身份认证,权限判别的功能。
一般而言,当你选用的是SSM,则选用shiro框架;选用Spring Boot,则使用Spring Security。

接口

从Spring Security的使用层面,下面介绍几大接口和类

1.类WebSecurityConfigurerAdapter

从名字上看,我们知道这是一个配置类,当我们需要使用Spring Security的各项功能时,我们需要继承该类,通过该类中的configure方法去配置程序需要进行权限管理的路径接口,认证成功或者失败需要跳转的处理器等功能。

下面是我项目中的一个例子(前后端分离)

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)//开启Controller权限注解必备
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
    
    

    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private AuthenticationSuccess authenticationSuccess;
    @Autowired
    private AuthenticationFailure authenticationFailure;
    @Autowired
    private MyAuthenticationEntryPoint authenticationEntryPoint;
    @Autowired
    private MyAccessDeniedHandler accessDeniedHandler;

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

    protected void configure(HttpSecurity http) throws Exception {
    
    
        http.formLogin() //自定义编写登录页面
                .usernameParameter("username").passwordParameter("password")
                .loginProcessingUrl("/chat/login") //登录跳转到哪个Controller.生效,删除报错,不删除,前端拿不到返回信息
                .successHandler(authenticationSuccess)
                .failureHandler(authenticationFailure)
                .and()
                .authorizeRequests()//认证请求
                .anyRequest().authenticated()
                .and().csrf().disable()
                .cors()
                .and()
                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
                .and()
                .exceptionHandling().accessDeniedHandler(accessDeniedHandler);
    }

    @Bean
    PasswordEncoder password() {
    
    
        return new BCryptPasswordEncoder();
    }

    /**
     * 跨域配置
     *
     * @return
     */
    @Bean
    public CorsFilter corsFilter() {
    
    
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("http://localhost:3000");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setAllowCredentials(true);

        source.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(source);
    }
}

解释:
首先我们先使用注解@Configuration将该配置类注入到Spring容器中。

接着我们其他方法先不看,重点关注protected void configure(HttpSecurity http) throws Exception {}方法:
1.该方法中首先使用formLogin方法,代表使用我们自己的登录界面,(Spring Security有默认的登录界面。)

2.后接.usernameParameter("username").passwordParameter("password") .loginProcessingUrl("/chat/login")表示我们登录界面表单中用户名,密码的配置项的key值。(注意:SpringSecurity只接收application/x-www-form-urlencoded格式的数据,本项目前端用Antd表单默认格式为application/json,搞了好久才找到这个原因!!!)

3..loginProcessingUrl("/chat/login")方法用于跳转登录验证的后端接口
4. 下面的四个方法分别是设定登录验证成功跳转的处理器,失败跳转的处理器,未登录跳转的处理器和无权限访问跳转的处理器。后面会挑一两个讲讲,其他的也就一样。

				.successHandler(authenticationSuccess)
                .failureHandler(authenticationFailure).exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
                .and()
                .exceptionHandling().accessDeniedHandler(accessDeniedHandler);

2.接口UserDetailsService

程序中设置一个类继承该接口,在该类中设置具体的比对逻辑,该接口用于框架比对配置中或者和数据库中的用户名密码是否一致。

例子:
该例子通过获取数据库中的用户名和密码,与用户输入的用户名进行比对,查看数据是否存在,如果存在,则获取其权限传参。

/**
 * 通过用户名判断是否存在该数据,返回数据库中该用户名的密码
 */
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
    
    

    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    
    
        UserInfo user = userMapper.getUserByName(username);
        String userName = user.getUsername();
        //此处将数据库中查询出来的密码进行加密,已便config中用加密的密码进行认证
        //String password = new BCryptPasswordEncoder().encode(user.getPassword());
        String password = user.getPassword();
        String role = user.getRole();
        if(user == null){
    
    
            throw new UsernameNotFoundException("用户名不存在");
        }
        //设置用户权限
        List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList(role);
        return new User(userName,password,auths);
    }
}

接着在WebSecurityConfigurerAdapter中运用下面的configure方法进行username和password的比对。

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

另外:model中的用户需要实现UserDetails接口

3.接口AuthenticationFailureHandler

该接口主要用来编写登录失败后应该如何处理的逻辑,具体例子如下:

由于我的项目是前后端分离,每次登录成功还是失败,都需要向前端返回相应的数据,让前端根据接收到的json数据串进行相应页面显示。该例子中ResponseJson是我自己编写的vo类,用于设置返回状态码和相应数据。在该方法中如果登录失败,则将相应信息写入json中返回。

登录成功处理器也大概类似这种写法。

@Component
public class AuthenticationFailure implements AuthenticationFailureHandler {
    
    

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

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
    
    
        //httpServletResponse.setStatus(500);
        System.out.println("登录失败");
        ResponseJson responseJson = new ResponseJson().error("用户名或者密码错误");
        httpServletResponse.setContentType("application/json;charset=UTF-8");
        PrintWriter printWriter = httpServletResponse.getWriter();
        printWriter.write(objectMapper.writeValueAsString(responseJson));
        printWriter.flush();
        printWriter.close();
    }
}

4.接口AccessDeniedHandler

该接口用户判断用户是否有访问某个接口的权限。

例子:
和其他处理器一样,都需要实现方法完成业务逻辑。

/**
 * 权限处理类
 */
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
    
    

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
    
    
        System.out.println("无权限访问");
        ResponseJson responseJson = new ResponseJson().error("无权限访问");
        response.setContentType("application/json;charset=UTF-8");
        PrintWriter printWriter = response.getWriter();
        printWriter.write(objectMapper.writeValueAsString(responseJson));
        printWriter.flush();
        printWriter.close();
    }
}

但是,我们如何操作在哪些特定接口才进行权限判断呢?
我们需要在Controller中用@PreAuthorize(“hasAuthority(‘admin’)”)开启对某个接口的权限判定

/**
     * 模糊查询信息(分页查询)
     *
     * @return
     */
    @CrossOrigin(origins = "*", maxAge = 3600)
    @RequestMapping(value = "/usersearchlist",method = RequestMethod.POST)
    @ResponseBody        //用于转换对象为JSON
    @PreAuthorize("hasAuthority('admin')")
    public ResponseJson usersearchlist(@RequestBody Map<String, String> data) {
    
    
        String key = data.get("name");
        List<UserInfo> list = chatService.userfindkeyAll(key);
        if(list.size()!=0){
    
    
            return new ResponseJson().success().setData("datalist", list);
        }return new ResponseJson().error("查找失败");
    }

注意:hasAuthority不需要在“admin”加前缀ROLE_;hasRole需要

最后在WebSecurityConfigurerAdapter中加入注解
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)//开启Controller权限注解必备
方可使用

最后

Spring Security中还有接口和类,比如:使用AuthenticationProvider实现自定义认证流程,使用SecurityContext可以获取和设置当前的认证对象等等

但本文基于使用的方面上说,上述几个接口已经足够实现基本功能,其他功能本项目无要求,所以没有使用,也就不谈了。有兴趣自己再研究研究叭。

猜你喜欢

转载自blog.csdn.net/weixin_43896829/article/details/115724555