Spring Security使用

本文例子基于前后端不分离项目

Maven依赖

<dependency>  
  <groupId>org.springframework.security</groupId>  
  <artifactId>spring-security-web</artifactId>  
  <version>5.2.1.RELEASE</version>  
</dependency>  
  
<dependency>  
  <groupId>org.springframework.security</groupId>  
  <artifactId>spring-security-config</artifactId>  
  <version>5.2.1.RELEASE</version>  
</dependency>

Java配置

@EnableWebSecurity  
public class SecurityConfig extends WebSecurityConfigurerAdapter {  
  
    private static final String QUERY_USER_SQL = "select username, password, enabled from t_user where username = ?";  
    private static final String QUERY_AUTHORITY_SQL = "select username, authority from t_authority inner join t_user on t_authority.user_id = t_user.id where username = ?";  
  
    private DataSource dataSource;  
  
    private AuthenticationSuccessHandler authenticationSuccessHandler;  
  
    @Autowired  
    public SecurityConfig(DataSource dataSource, AuthenticationSuccessHandler authenticationSuccessHandler) {  
        this.dataSource = dataSource;  
        this.authenticationSuccessHandler = authenticationSuccessHandler;  
    }  

    // 表单验证相关配置
    @Override  
    protected void configure(HttpSecurity http) throws Exception {  
        http.authorizeRequests()  
            .antMatchers("/static/common/**", "/static/template/**", "/signUp", "/signUp/*").permitAll()  
            .anyRequest().authenticated()  
            .and().formLogin().loginPage("/signIn").permitAll()  
            .successHandler(authenticationSuccessHandler)  
            .failureUrl("/signInError")  
            .and().logout().logoutUrl("/signOut").logoutSuccessUrl("/signIn")
            .and().rememberMe().key("greenbean").tokenValiditySeconds(259200)
            .authenticationSuccessHandler(authenticationSuccessHandler);
    }  

    // 用户数据来源配置
    @Override  
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {  
        auth.jdbcAuthentication().dataSource(dataSource)  
            .usersByUsernameQuery(QUERY_USER_SQL)  
            .authoritiesByUsernameQuery(QUERY_AUTHORITY_SQL);  
        // 这里可以改为配置userDetailsService
    }  
  
    @Bean  
    public PasswordEncoder passwordEncoder() {  
        return new BCryptPasswordEncoder();  
    }  
}

登录成功后处理

@Component  
public class SignInSuccessHandler implements AuthenticationSuccessHandler {  
  
    private UserService userService;  
  
    @Autowired  
    public SignInSuccessHandler(UserService userService) {  
        this.userService = userService;  
    }  
  
    public SignInSuccessHandler() {  
    }  
  
    @Override  
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException {  
        String username = authentication.getName();  
        User user = userService.getUserByUsername(username);  
  
        HttpSession session = httpServletRequest.getSession();  
        session.setAttribute("userId", user.getId());  
        session.setAttribute("userNickname", user.getNickname());  
        session.setAttribute("userAvatar", user.getAvatar());  
  
        httpServletResponse.sendRedirect("home");  
    }  
}

controller配置

@Controller  
public class SignController {  
    @RequestMapping(value = "/signIn", method = RequestMethod.GET)  
    public String signIn() {  
        return "signIn";  
    }  

    // 登录失败则添加提示信息,返回登录页并显示提示信息
    @RequestMapping("/signInError")  
    public String signInError(Model model) {  
        model.addAttribute("signInError", true);  
        return "signIn";  
    }
}

整合Thymeleaf

登录页

<!DOCTYPE html>  
<html lang="en" xmlns:th="http://www.thymeleaf.org">  
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
    <h1>Login page</h1>
    <p th:if="${loginError}" class="error">Wrong user or password</p>
    <form th:action="@{/login}" action="login" method="post">
        <label for="username">Username</label>:
        <input type="text" id="username" name="username" autofocus="autofocus" /> <br />
        <label for="password">Password</label>:
        <input type="password" id="password" name="password" /> <br />
        <input type="submit" value="Log in" />
        <input type="checkbox" name="remember-me"> Remember me
    </form>
</body>
</html>

登出

截取主页登出部分代码

<a class="dropdown-item" id="signOutLink" href="javascript:void(0);">Sign out</a>
<!-- 开启CSRF防护后,登出必须是POST请求 -->
<form id="signOutForm" style="display: none" method="post" th:action="@{/signOut}"></form>
(function() {  
    $(function() {  
        const signOutLink = $('#signOutLink');  
        signOutLink.on("click", function () {  
            $('#signOutForm').trigger("submit");  
        });  
    });  
})();

参考文档