Hướng dẫn sử dụng Spring Security với Spring MVC 5
Hướng dẫn sử dụng Spring Security với Spring MVC 5 sẽ trình bày cách tạo một form login trong Spring MVC 5 với Spring Security.
Chúng tôi sẽ kết hợp với Project đã tạo ở bài Hướng dẫn xử lý database trong Spring MVC 5 với Spring Data JPA để xử lý tiếp.
Khi truy cập website, người dùng được yêu cầu đăng nhập trước khi thực hiện xem hoặc thêm một khách hàng
Hướng dẫn sử dụng Spring Security với Spring MVC 5 – Các bước thực hiện
1/ Tạo một Maven Web Application Project
Các bạn có thể tạo mới một Project. Trong bài hướng dẫn này, chúng tôi sử dụng lại project của bài Hướng dẫn xử lý database trong Spring MVC 5 với Spring Data JPA
2/ Mở file pom.xml và thêm dependency
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.security.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.security.version}</version>
</dependency>
3/ Tạo thêm 2 table của database demoDB
-- Bảng users
create table users(
username varchar(50) not null primary key,
password varchar(100) not null,
enabled boolean not null
);
-- Bảng role
create table authorities (
username varchar(50) not null,
authority varchar(50) not null,
constraint fk_authorities_users foreign key(username) references users(username)
);
create unique index ix_auth_username on authorities (username,authority);
Thêm thông tin đăng nhập vào database
-- Thông tin user (username là admin, password là admin@123
insert into users(username,password,enabled)
values('admin','$2a$10$hbxecwitQQ.dDT4JOFzQAulNySFwEpaFLw38jda6Td.Y/cOiRzDFu',true);
-- Role của user
insert into authorities(username,authority) values('admin','ROLE_ADMIN');
4/ Tạo một model là một Java class và đặt tên User
Nhập code cho User
5/ Tạo một model là một Java class và đặt tên Authorities
Nhập code cho Authorities
6/ Tạo một Repository là một Interface và đặt tên là UserRepository
Nhập code cho UserRepositoy
7/ Tạo một DTO là một Java class và đặt tên là UserDetailsDTO
Nhập code cho UserDetailsDTO
package vn.giasutinhoc.spring5mvc.jpa.dto;
import java.util.Collection;
import java.util.stream.Collectors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import vn.giasutinhoc.spring5mvc.jpa.model.User;
public class UserDetailsDTO implements UserDetails {
private User user;
public UserDetailsDTO(User user) {
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities().stream()
.map(authority -> new SimpleGrantedAuthority(authority.getAuthority().toString()))
.collect(Collectors.toList());
}
public User getUserDetails() {
return user;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
8/ Tạo một service là một Java class và đặt tên UserDetailsServiceImpl
Nhập code cho UserDetailsServiceImpl
9/ Cấu hình Spring Security
Tạo một Java class và đặt tên WebSecurityConfig
Nhập code cho WebSecurityConfig
package vn.giasutinhoc.spring5mvc.jpa.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
};
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider());
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/customer/**").hasAnyRole("ADMIN", "USER")
.antMatchers("/user/**", "/resource/**").permitAll()
.and().formLogin().loginPage("/user/login")
.usernameParameter("username").passwordParameter("password")
.loginProcessingUrl("/user/postLogin").permitAll()
.failureUrl("/user/loginFailed").and().logout().logoutUrl("/user/doLogout")
.logoutSuccessUrl("/user/logout").permitAll()
.and().csrf().disable();
}
}
10/ Tạo tiếp một Java class và đặt tên SecurityWebApplicationInitializer
Nhập code cho SecurityWebApplicationInitializer
11/ Mở WebMvcConfig.java và bổ sung code
12/ Mở AppInitializer.java và thêm code
13/ Tạo một controller là một Java class và đặt tên UserController
Nhập code cho UserController
package vn.giasutinhoc.spring5mvc.jpa.controller;
import java.security.Principal;
import javax.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.ModelAndView;
import vn.giasutinhoc.spring5mvc.jpa.dto.UserDetailsDTO;
import vn.giasutinhoc.spring5mvc.jpa.model.User;
@SessionAttributes({ "currentUser" })
@Controller
@RequestMapping("user")
public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class);
/**
* This method handles login GET requests. If users is already logged-in and
* tries to goto login page again, will be redirected to list page.
*/
@GetMapping("login")
public String loginPage(Principal principal) {
return principal == null ? "login" : "redirect:/";
}
@PostMapping("postLogin")
public void postLogin(Model model, HttpSession session) {
log.info("postLogin()");
// read principal out of security context and set it to session
UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder
.getContext().getAuthentication();
validatePrinciple(authentication.getPrincipal());
User loggedInUser = ((UserDetailsDTO) authentication.getPrincipal()).getUserDetails();
model.addAttribute("currentUser", loggedInUser.getUsername());
session.setAttribute("userId", loggedInUser.getUsername());
}
private void validatePrinciple(Object principal) {
if (!(principal instanceof UserDetailsDTO)) {
throw new IllegalArgumentException("Principal can not be null!");
}
}
@RequestMapping(value = "loginFailed", method = RequestMethod.GET)
public String loginError(Model model) {
log.info("Login attempt failed");
model.addAttribute("error", "true");
return "login";
}
/**
* This method handles logout requests. Toggle the handlers if you are
* RememberMe functionality is useless in your app.
*/
@GetMapping("logout")
public ModelAndView logoutPage(SessionStatus session) {
SecurityContextHolder.getContext().setAuthentication(null);
session.setComplete();
return new ModelAndView("login");
}
}
14/ Tạo một view là một JSP file và đặt tên login
Nhập code cho login.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Đăng nhập</title>
<link href="<c:url value="/resources/css/bootstrap.min.css" />"
rel="stylesheet">
<%@ page isELIgnored="false"%>
<script src="<c:url value="/resources/js/jquery-1.11.1.min.js" />"></script>
<script src="<c:url value="/resources/js/bootstrap.min.js" />"></script>
</head>
<body>
<div class="container">
<div class="col-md-12">
<div class="panel panel-info">
<div class="panel-heading">
<div class="panel-title">
<legend>Vui lòng đăng nhập</legend>
<c:if test="${not empty error}">
<div class="alert alert-danger">
<spring:message code="AbstractUserDetailsAuthenticationProvider.badCredentials"/>
<br/>
</div>
</c:if>
</div>
</div>
<div class="panel-body">
<form action="postLogin" method="post">
<div class="form-group">
<input class="form:input-large" placeholder="User Name"
name='username' type="text">
</div>
<div class="form-group">
<input class=" form:input-large" placeholder="Password"
name='password' type="password" value="">
</div>
<input class="btn" type="submit" value="Đăng nhập">
</form>
</div>
</div>
</div>
</body>
</html>
15 Tạọ một messages.properties
Nhập code cho messages.properties
Hướng dẫn sử dụng Spring Security với Spring MVC 5 – Build, Deploy và Run
1/ Về cách build, deploy và run các bạn tham khảo bài Hướng dẫn tạo project sử dụng Spring MVC 5 với eclipse
2/ Kết quả khi truy cập http://localhost:8080/SpringMVC_JPA/user/login
Sai thông tin đăng nhập
Đúng thông tin đăng nhập với user là admin, password là admin@123