Spring Boot 1.5.9.RELEASE Java 8 Maven 4.0.0
Use Spring Security to verify and authenticate with the login information in the DB. DB uses H2DB and ORM uses Doma.
Generate a project with Spring Inirializr.
Since we will use Doma for ORM this time, set the annotation processing.
Only those that have been changed from the default are listed.
pom.xml(※Excerpt)
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.seasar.doma.boot/doma-spring-boot-starter -->
<dependency>
<groupId>org.seasar.doma.boot</groupId>
<artifactId>doma-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
UserEntity.java
package com.example.springbootsecuritysample.entity;
import java.util.Collection;
import org.seasar.doma.Entity;
import org.seasar.doma.Id;
import org.seasar.doma.jdbc.entity.NamingType;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import lombok.Getter;
import lombok.Setter;
/**
*Entity in USER table
* @author T.Harao
*
*/
@Entity(naming = NamingType.SNAKE_UPPER_CASE)
@Getter
@Setter
public class UserEntity implements UserDetails {
@Id
private String userId;
private String password;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getUsername() {
return userId;
}
/**
*Return the password to be checked by UserDetailsService
*If you are using Lombok, if the field has "password"
*GetPassword with @Getter()Is not required because it will generate
*/
@Override
public String getPassword() {
return password;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
UserDao.java
package com.example.springbootsecuritysample.dao;
import org.seasar.doma.Dao;
import org.seasar.doma.Select;
import org.seasar.doma.boot.ConfigAutowireable;
import com.example.springbootsecuritysample.entity.UserEntity;
/**
*DAO accessing the USER table
* @author T.Harao
*
*/
@Dao
@ConfigAutowireable
public interface UserDao {
@Select
public UserEntity selectByUserId(String userId);
}
AuthService.java
package com.example.springbootsecuritysample.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.example.springbootsecuritysample.dao.UserDao;
import com.example.springbootsecuritysample.entity.UserEntity;
/**
*Service that handles authentication
* @author T.Harao
*
*/
@Service
public class AuthService implements UserDetailsService {
@Autowired
private UserDao dao;
/**
*User loading
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if(username == null || "".equals(username)) {
throw new UsernameNotFoundException("No user ID entered");
}
UserEntity user = dao.selectByUserId(username);
if(user == null) {
throw new UsernameNotFoundException("The user ID is invalid.");
}
return user;
}
}
IndexForm.java
package com.example.springbootsecuritysample.form;
import org.hibernate.validator.constraints.NotEmpty;
import lombok.Getter;
import lombok.Setter;
/**
*Form used by IndexController
* @author T.Harao
*
*/
@Getter
@Setter
public class IndexForm {
@NotEmpty
private String userId;
@NotEmpty
private String password;
}
IndexController.java
package com.example.springbootsecuritysample.controller;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import com.example.springbootsecuritysample.form.IndexForm;
/**
*Login
* @author T.Harao
*
*/
@Controller
@RequestMapping({"/", "/index"})
public class IndexController {
@ModelAttribute
public IndexForm initForm(){
return new IndexForm();
}
/**
*Initial display
* @param mv
* @return
*/
@RequestMapping(value = {"/", "/index"}, method = RequestMethod.GET)
public ModelAndView index(ModelAndView mv) {
mv.setViewName("index/index");
return mv;
}
/**
*At the time of authentication error
* @param mv
* @return
*/
@RequestMapping(value = {"/", "/index"}, method = RequestMethod.POST)
public ModelAndView login(@ModelAttribute @Validated IndexForm form, BindingResult result, ModelAndView mv) {
if(!result.hasErrors()) {
mv.addObject("errorMessage", "Incorrect login information");
}
mv.setViewName("index/index");
return mv;
}
}
MenuController.java
package com.example.springbootsecuritysample.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
/**
*menu
* @author T.Harao
*
*/
@Controller
@RequestMapping("/menu")
public class MenuController {
/**
*Initial display
* @param mv
* @return
*/
@RequestMapping(value = {"/", "/index"}, method = RequestMethod.GET)
public ModelAndView index(ModelAndView mv) {
mv.setViewName("menu/index");
return mv;
}
}
FailureHandler.java
package com.example.springbootsecuritysample.config.handler;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
/**
*Handler when authentication fails
* @author T.Harao
*
*/
@Component
public class FailureHandler implements AuthenticationFailureHandler {
/**
*When authentication fails
*/
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
//「/Forward to
RequestDispatcher dispatch = request.getRequestDispatcher("/");
dispatch.forward(request, response);
}
}
SuccessHandler.java
package com.example.springbootsecuritysample.config.handler;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
/**
*Handler when authentication is successful
* @author T.Harao
*
*/
@Component
public class SuccessHandler implements AuthenticationSuccessHandler {
/**
*When authentication is successful
*/
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
//「/menu/Redirect to
response.sendRedirect(request.getContextPath() + "/menu/");
}
}
SecurityConfig.java
package com.example.springbootsecuritysample.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import com.example.springbootsecuritysample.config.handler.FailureHandler;
import com.example.springbootsecuritysample.config.handler.SuccessHandler;
import com.example.springbootsecuritysample.service.AuthService;
/**
*security settings
* @author T.Harao
*
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthService service;
@Autowired
private FailureHandler failureHandler;
@Autowired
private SuccessHandler successHandler;
/**
*Web Security settings
*/
@Override
public void configure(WebSecurity web) throws Exception {
//Static resources(images、css、javascript)And access to the H2DB console ignores security settings
web.ignoring().antMatchers("/css/**", "/fonts/**", "/images/**", "/js/**", "/h2-console/**");
}
/**
*HttpSecurity settings
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//Authorization settings
http.authorizeRequests()
//Set a URL that can be accessed without authentication
.antMatchers("/", "/index/**").permitAll()
//Settings other than the above required for authentication
.anyRequest().authenticated();
//Login settings
http.formLogin()
//Set the path for authentication processing
.loginProcessingUrl("/index/login")
//Set login form path
.loginPage("/")
.loginPage("/index/**")
//Set the URL to redirect when authentication is successful
.defaultSuccessUrl("/menu/")
//Set the URL to forward when authentication fails
.failureForwardUrl("/")
//Set the URL to forward when authentication is successful
//.successForwardUrl("/")
//Set the handler class to be called when authentication is successful
//.successHandler(successHandler)
//Set the URL to redirect when authentication fails
//.failureUrl("/menu/")
//Set handler class to be called when authentication fails
//.failureHandler(failureHandler)
//Set user name and password parameter name
.usernameParameter("userId").passwordParameter("password");
}
/**
*Setting
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//Set "NoOpPasswordEncoder" because the password is registered in the DB in plain text.
auth.userDetailsService(service)
.passwordEncoder(NoOpPasswordEncoder.getInstance());
}
}
layout.html
<!DOCTYPE html>
<html
xmlns = "http://www.w3.org/1999/xhtml"
xmlns:th = "http://www.thymeleaf.org"
xmlns:layout = "http://www.ultraq.net.nz/thymeleaf/layout"
>
<head>
<meta charset="UTF-8" />
<title layout:title-pattern="$DECORATOR_TITLE - $CONTENT_TITLE">Spring Security test</title>
<link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css" th:href="@{/css/bootstrap.min.css}" media="all" />
<link rel="stylesheet" type="text/css" href="/css/bootstrap-theme.min.css" th:href="@{/css/bootstrap-theme.min.css}" media="all" />
<script type="text/javascript" src="/js/jquery-1.12.4.min.js" th:src="@{/js/jquery-1.12.4.min.js}"></script>
<script type="text/javascript" src="/js/bootstrap.min.js" th:src="@{/js/bootstrap.min.js}"></script>
</head>
<body>
<div class="contents" layout:fragment="contents"></div>
</body>
</html>
index/index.html
<!DOCTYPE html>
<html
xmlns = "http://www.w3.org/1999/xhtml"
xmlns:th = "http://www.thymeleaf.org"
xmlns:layout = "http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layout"
>
<head>
<title>Login</title>
</head>
<body>
<div layout:fragment="contents">
<form class="form-horizontal" method="POST" action="/index/login/" th:action="@{/index/login}" th:object="${indexForm}">
<div th:text="${errorMessage}?: ''" class="col-sm-offset-2 text-danger"></div>
<div class="form-group">
<p th:if="${#fields.hasErrors('*{userId}')}" th:errors="*{userId}" class="col-sm-offset-2 text-danger"></p>
<label for="user-id" class="col-sm-2 control-label">User ID</label>
<div class="col-sm-5">
<input type="text" class="form-control" id="user-id" th:field="*{userId}" placeholder="User ID" />
</div>
</div>
<div class="form-group">
<p th:if="${#fields.hasErrors('*{password}')}" th:errors="*{password}" class="col-sm-offset-2 text-danger"></p>
<label for="password" class="col-sm-2 control-label">password</label>
<div class="col-sm-5">
<input type="password" class="form-control" id="password" th:field="*{password}" placeholder="password" />
</div>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary col-sm-2 col-sm-offset-2" name="login" value="Login" />
<input type="reset" class="btn btn-default col-sm-2 col-sm-offset-1" name="clear" value="clear" />
</div>
</form>
</div>
</body>
</html>
menu/index.html
<!DOCTYPE html>
<html
xmlns = "http://www.w3.org/1999/xhtml"
xmlns:th = "http://www.thymeleaf.org"
xmlns:layout = "http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="layout"
>
<head>
<title>menu</title>
</head>
<body>
<div layout:fragment="contents">
<h2>menu</h2>
</div>
</body>
</html>
application.yaml
#spring
spring:
profiles:
active: dev
datasource:
url: jdbc:h2:./db
#server
server:
contextPath: /security-sample
#doma
doma:
dialect: h2
application-dev.yaml
#spring
spring:
h2:
console:
enabled: true
thymeleaf:
cache: false
application-production.yaml
#spring
spring:
h2:
console:
enabled: false
thymeleaf:
cache: true
selectByUserId.sql
select
user_id
,password
from
user
where
user_id = /*userId*/''
schema.sql
--drop table if exists user;
create table if not exists user (
user_id varchar(30) not null primary key
,password varchar(30) not null
);
data.sql
insert into user (user_id,password) values ('test','pass');
The folder structure is as follows.
Go to [http: // localhost: 8080 / security-sample /](http: // localhost: 8080 / security-sample /) and go to
Enter test
as the user ID and pass
as the password to move to the menu screen.
Login screen
Menu screen
The processing when authentication succeeds or fails is described in the following part of SecurityConfig.java
.
SecurityConfig.java
/**
*HttpSecurity settings
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//Authorization settings
http.authorizeRequests()
//Set a URL that can be accessed without authentication
.antMatchers("/", "/index/**").permitAll()
//Settings other than the above required for authentication
.anyRequest().authenticated();
//Login settings
http.formLogin()
//Set the path for authentication processing
.loginProcessingUrl("/index/login")
//Set login form path
.loginPage("/")
.loginPage("/index/**")
//Set the URL to redirect when authentication is successful
.defaultSuccessUrl("/menu/")
//Set the URL to forward when authentication fails
.failureForwardUrl("/")
//Set the URL to forward when authentication is successful
//.successForwardUrl("/")
//Set the handler class to be called when authentication is successful
//.successHandler(successHandler)
//Set the URL to redirect when authentication fails
//.failureUrl("/menu/")
//Set handler class to be called when authentication fails
//.failureHandler(failureHandler)
//Set user name and password parameter name
.usernameParameter("userId").passwordParameter("password");
}
I have commented out, but when authentication succeeds and fails, You can redirect, forward, and delegate processing to the handler class.
The project created this time is in here
Recommended Posts