Introduction and simple use of Spring Security

foreword

As a Spring-based security framework, Spring Security has the functions of identity authentication and authority discrimination.
Generally speaking, when you choose SSM, you choose the shiro framework; if you choose Spring Boot, you use Spring Security.

interface

From the use level of Spring Security, the following introduces several major interfaces and classes

1. Class WebSecurityConfigurerAdapter

From the name, we know that this is a configuration class. When we need to use various functions of Spring Security, we need to inherit this class, and use the configure method in this class to configure the path interface that the program needs to manage permissions, authentication Success or failure needs to jump to the processor and other functions.

The following is an example in my project (front and back end separation)

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

Explanation:
First, we use the annotation @Configuration to inject the configuration class into the Spring container.

Then let’s ignore the other methods and focus on protected void configure(HttpSecurity http) throws Exception {}the method:
1. In this method, the formLogin method is used first, which means using our own login interface. (Spring Security has a default login interface.)

2. Followed by .usernameParameter("username").passwordParameter("password") .loginProcessingUrl("/chat/login")the key value of the configuration item indicating the user name and password in the form of our login interface. (Note: SpringSecurity only accepts data in application/x-www-form-urlencoded format. The default format of the Antd form used in the front end of this project is application/json. It took a long time to find out the reason!!!)

3. .loginProcessingUrl("/chat/login")The method is used to jump to the back-end interface of login verification.
4. The following four methods are to set the processor for successful jump of login verification, the processor for failed jump, the processor for non-login jump and no permission Handler for access jumps. I will pick one or two to talk about later, and the others will be the same.

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

2. Interface UserDetailsService

Set a class in the program to inherit this interface, and set specific comparison logic in this class. This interface is used in the framework comparison configuration or whether it is consistent with the username and password in the database.

Example:
This example obtains the user name and password in the database, compares it with the user name entered by the user, checks whether the data exists, and obtains its permission parameter if it exists.

/**
 * 通过用户名判断是否存在该数据,返回数据库中该用户名的密码
 */
@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);
    }
}

Then use the following configure method in WebSecurityConfigurerAdapter to compare username and password.

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

In addition: users in the model need to implement the UserDetails interface

3. Interface AuthenticationFailureHandler

This interface is mainly used to write the logic of how to deal with login failures. The specific examples are as follows:

Since my project is separated from the front and back ends, every time the login succeeds or fails, it needs to return the corresponding data to the front end, so that the front end can display the corresponding page according to the received json data string. In this example, ResponseJson is a vo class written by myself, which is used to set the return status code and corresponding data. In this method, if the login fails, the corresponding information will be written into json and returned.

The login success processor is probably similar to this writing method.

@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. Interface AccessDeniedHandler

The interface user judges whether the user has the permission to access an interface.

Example:
Like other processors, methods need to be implemented to complete business logic.

/**
 * 权限处理类
 */
@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();
    }
}

However, how do we operate on which specific interfaces to judge permissions?
We need to use @PreAuthorize("hasAuthority('admin')") in the Controller to enable the authority determination of an interface

/**
     * 模糊查询信息(分页查询)
     *
     * @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("查找失败");
    }

Note: hasAuthority does not need to prefix "admin" with ROLE_; hasRole does

Finally, add annotations to WebSecurityConfigurerAdapter
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)//Open the Controller permission annotation
before it can be used

at last

There are also interfaces and classes in Spring Security, such as: using AuthenticationProvider to implement a custom authentication process, using SecurityContext to get and set the current authentication object, etc.

However, based on the aspect of use, this article says that the above-mentioned interfaces are sufficient to realize the basic functions, and other functions are not required by this project, so we will not talk about them if they are not used. If you are interested, do your own research.

Guess you like

Origin blog.csdn.net/weixin_43896829/article/details/115724555