存Spring Security Login
建造一個Web不免的需要登入系統,登入系統不外乎就是使用Filter攔截每次的Request,判斷Session是否存在,若沒有則引導至登入頁面,這邊通常會加上一些控管,如timeout、repeat login等等問題,這些都是需要好好管控的,那在這邊我將會說明如何使用Security登入與登出。
POM
Security Config
package com.mvc.example.webconfig;
import javax.sql.DataSource;
import org.dom4j.swing.BranchTreeNode;
import org.hibernate.boot.registry.selector.SimpleStrategyRegistrationImpl;
import org.hibernate.loader.plan.exec.process.spi.ReturnReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.StandardPasswordEncoder;
import com.mvc.example.service.CustomUserDetailsService;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("andy").password("andy").roles("USER");
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN");
auth.inMemoryAuthentication().withUser("dba").password("dba").roles("ADMIN", "DBA");
}
@Bean
public SessionRegistry sessionRegistry() {
SessionRegistry sessionRegistry = new SessionRegistryImpl();
return sessionRegistry;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll() //全部的人都可以嘗試登入
.and()
.formLogin() //這邊如果沒有使用下面的設定,Spring會自行產生簡易的登入頁面
.loginPage("/login") //登入頁面位置
.usernameParameter("username") //表單帳號參數名稱
.passwordParameter("password") //表單密碼參數名稱
.defaultSuccessUrl("/") //登入完成後導引至首頁
.permitAll() //此頁面沒有權限限制
.and()
.logout()
.logoutSuccessUrl("/login") //登出後跳轉至登入頁面
.invalidateHttpSession(true) //使該Session無效
.and()
.csrf() //使用csrf防禦機制
.and()
.sessionManagement() //管理Session
.maximumSessions(1) //同時只能一個Session
.maxSessionsPreventsLogin(true) //只能讓一個帳號存在一個Session
.sessionRegistry(sessionRegistry()) //監聽Session的欄位,基本上預設即可,若要做更多用途可以自行撰寫
.and()
.and()
.exceptionHandling().accessDeniedPage("/403"); //將所有403頁面導向這裡
}
}
SecurityInit
這邊我也算是被雷到了,當初設定完後發現,登出後,居然就無法再登入了,原因就是這地方沒有做設定,詳細原因沒有查詢,有興趣自己看看唷!! 這邊是可以客製化的。
public class SecurityInit extends AbstractSecurityWebApplicationInitializer {
@Override
protected boolean enableHttpSessionEventPublisher() {
return true;
}
}
User Controller
@Controller
public class UserController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public String homePage(ModelMap model) {
model.addAttribute("greeting", "Hi, Welcome to mysite");
return "welcome";
}
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginPage() {
return "login";
}
@RequestMapping(value="/logout", method = RequestMethod.GET)
public String logoutPage (HttpServletRequest request, HttpServletResponse response, SessionRegistryImpl sessionRegistryImpl ) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
SecurityContextLogoutHandler securityContextLogoutHandler = new SecurityContextLogoutHandler();
securityContextLogoutHandler.setInvalidateHttpSession(true);
securityContextLogoutHandler.logout(request, response, auth);
}
return "redirect:/login?logout";
}
}
JSP
welcome
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Hello Spring!</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div style="width:100%; background-color:#eee; padding: 12px 16px 0 0;">
<security:authorize access="authenticated" var="authenticated">
<security:authentication property="principal.username" var="username"/>
<a href="/logout" style="float: right;"><button class="btn btn-danger">Logout</button></a>
</security:authorize>
<%if(!(Boolean)pageContext.getAttribute("authenticated")) {%>
<a href="/login" style="float: right; margin-right: 12px;"><button class="btn btn-success" style="">Login</button></a>
<%} %>
</div>
<div class="jumbotron text-center">
<h1>My Welcome Page</h1>
</div>
<div class="container">
<div class="row">
<div class="col-sm-4">
<h3>Column 1</h3>
<p>Lorem ipsum dolor..</p>
<p>Ut enim ad..</p>
</div>
<div class="col-sm-4">
<h3>Column 2</h3>
<p>Lorem ipsum dolor..</p>
<p>Ut enim ad..</p>
</div>
<div class="col-sm-4">
<h3>Column 3</h3>
<p>Lorem ipsum dolor..</p>
<p>Ut enim ad..</p>
</div>
</div>
</div>
</body>
</html>
login
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Login page</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<security:authorize access="authenticated" var="authenticated"></security:authorize>
<%if((Boolean)pageContext.getAttribute("authenticated")) {
response.sendRedirect("/");
} %>
<div style="width: 100%; height:100%; display: flex; align-items: center;">
<div style="width: 35%; margin: 0 auto; box-shadow: 6px 6px 1px gray; padding: 28px;">
<h3 style="text-align: center; margin-bottom: 20px;">Sign in to Spring Security</h3>
<div class="login-form" style="margin: 0 auto; width: 100%;" >
<c:url var="loginUrl" value="/login" />
<form action="${loginUrl}" method="post" class="form-horizontal" style="margin: 0px;">
<c:if test="${param.error != null}">
<div class="alert alert-danger">
<p>Invalid username and password.</p>
</div>
</c:if>
<c:if test="${param.logout != null}">
<div class="alert alert-success">
<p>You have been logged out successfully.</p>
</div>
</c:if>
<label style="color: gray;">UserName</label>
<input type="text" class="form-control" style="margin-bottom: 12px;" id="username" name="username" required>
<label style="color: gray;">Password</label>
<input type="password" class="form-control" style="margin-bottom: 16px;" id="password" name="password" required>
<input type="hidden" name="${_csrf.parameterName}"
value="${_csrf.token}" />
<input type="submit" class="btn btn-block btn-primary btn-default" value="Log in">
</form>
</div>
</div>
</div>
</body>
</html>