spring boot에서는 spring security를 적용하여 인증 및 인가를 구현했다.
우선 메인화면인 홈화면과 날씨는 하기와 같이 모든 접근을 허용하고자 한다.


'호두스케쥴러'의 핵심 기능인 [일정]과 [다이어리]는 접근 시 하기와 같은 로그인 화면이 뜨고 로그인을 한 유저에게만 접근을 허용한다.



또한 유저의 role은 2가지가 있는데 모든 권한을 가지고 있는 ADMIN(관리자)와 관리자 권한이 없이 사용자 권한을 가지는 MEMBER(사용자)가 있다.
관리자 계정으로 로그인 시 홈화면에는 '관리자페이지'로 이동할 수 있는 링크가 뜨고 클릭 시 관리자 페이지로 넘어간다.


사용자 계정으로 로그인 시 홈화면에는 '마이페이지'로 이동할 수 있는 링크가 뜨고 클릭 시 마이페이지로 넘어간다.


만약, 사용자가 URL로 관리자 페이지 접근을 시도하면 하기와 같이 error페이지가 나온다.

이제 소스코드를 보겠다.
제일 먼저 spring security 설정을 하기와 같이 했다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@EnableWebSecurity
@Configuration
public class SpringSecurityConfig {
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public WebSecurityCustomizer configure() {
return (web) -> web.ignoring().requestMatchers(
"/css/**", "/js/**", "/img/**", "/error/**", "/favicon.ico", "/common/**"
);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests((authorizeHttpRequests) -> authorizeHttpRequests
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/diary/**").authenticated() // 권한확인
.requestMatchers("/task/**").authenticated()
.anyRequest().permitAll()
)
.formLogin(form -> form
.loginPage("/user/login")
.loginProcessingUrl("/user/login")
.permitAll()
.usernameParameter("username")
.passwordParameter("password")
.defaultSuccessUrl("/", true)
.failureForwardUrl("/error/error")
).logout(logout -> logout
.logoutRequestMatcher(new AntPathRequestMatcher("/user/logout"))
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
.logoutSuccessUrl("/"))
.exceptionHandling((exception) -> exception.accessDeniedPage("/error/error"));
return http.build();
}
}
import com.multi.hodooScheduler.user.model.dto.UserDTO;
import com.multi.hodooScheduler.user.service.UserService;
import jakarta.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
@SessionAttributes("loginUser")
@Controller
@RequestMapping("/user")
public class UserController {
private UserService userService;
@Autowired
public UserController(UserService userService) {
super();
this.userService = userService;
}
@RequestMapping("/mypage")
public String mypage(){
return "user/userPage";
}
@GetMapping("/login")
public void loginPage(){
}
@PostMapping("/login")
public String loginUser(UserDTO userDTO, Model model, HttpSession session) {
String page = "redirect:/";
System.out.println(userDTO);
try {
UserDTO loginDTO = userService.loginUser(userDTO);
model.addAttribute("loginUser", loginDTO);
session.setAttribute("msg", "로그인 성공!");
} catch (Exception e) {
page = "common/errorPage";
session.setAttribute("msg", "로그인 실패!");
e.printStackTrace();
}
return page;
}
@GetMapping("/register")
public String register() {
return "user/registerform";
}
@PostMapping("/register")
public String insertUser(UserDTO userDTO, Model model, HttpSession session) {
String page = "";
try {
int result = userService.insertUser(userDTO);
if (result > 0) {
session.setAttribute("msg", "회원가입 성공!");
page = "user/login";
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
page = "common/errorPage";
}
return page;
}
@RequestMapping("/logout")
public String logout(HttpSession session) {
session.invalidate();
return "redirect:/";
}
@RequestMapping("/idCheck.do")
@ResponseBody
public int idCheck(UserDTO userDTO) { // 아이디 중복체크!
System.out.println(userDTO);
int result = 0;
try {
UserDTO checkId = userService.idCheck(userDTO);
if(checkId != null) {
result = 1;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
}
import com.multi.hodooScheduler.user.model.dto.UserDTO;
public interface UserService {
UserDTO loginUser(UserDTO userDTO) throws Exception;
int insertUser(UserDTO userDTO) throws Exception;
UserDTO idCheck(UserDTO userDTO) throws Exception;
UserDTO findUserById(String userId) throws Exception;
}
import com.multi.hodooScheduler.user.model.dao.UserDAO;
import com.multi.hodooScheduler.user.model.dto.UserDTO;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
private SqlSessionTemplate sqlSession;
private UserDAO userDAO;
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Autowired
public UserServiceImpl(SqlSessionTemplate sqlSession, UserDAO userDAO,
BCryptPasswordEncoder bCryptPasswordEncoder) {
super();
this.sqlSession = sqlSession;
this.userDAO = userDAO;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
@Override
public UserDTO loginUser(UserDTO userDTO) throws Exception {
String encpw = userDAO.selectEncryptedPwd(sqlSession, userDTO);
if (!bCryptPasswordEncoder.matches(userDTO.getPw(), encpw)) {
throw new Exception("패스워드 불일치, 로그인 실패");
}
UserDTO loginDto = userDAO.loginUser(sqlSession, userDTO);
if (loginDto == null) {
throw new Exception("loginUser 정보 확인, 로그인 실패");
}
return loginDto;
}
@Override
public UserDTO findUserById(String userId) throws Exception {
UserDTO userDTO = userDAO.findUserById(sqlSession, userId);
if(userDTO == null){
throw new Exception("일치하는 회원이 없습니다.");
}
return userDTO;
}
@Override
public int insertUser(UserDTO userDTO) throws Exception {
String encpw = bCryptPasswordEncoder.encode(userDTO.getPw());
int result = userDAO.insertUser(sqlSession, userDTO);
return result;
}
@Override
public UserDTO idCheck(UserDTO userDTO) throws Exception {
UserDTO loginDto = userDAO.loginUser(sqlSession, userDTO);
return loginDto;
}
}
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;
import com.multi.hodooScheduler.user.model.dto.UserDTO;
@Repository
public class UserDAO {
public UserDTO loginUser(SqlSessionTemplate sqlSession, UserDTO userDTO) {
return sqlSession.selectOne("userMapper.selectUser", userDTO);
}
public int insertUser(SqlSessionTemplate sqlSession, UserDTO userDTO) {
return sqlSession.insert("userMapper.insertUser", userDTO);
}
public String selectEncryptedPwd(SqlSessionTemplate sqlSession, UserDTO userDTO) {
return sqlSession.selectOne("userMapper.selectEncryptedPwd", userDTO);
}
public UserDTO findUserById(SqlSessionTemplate sqlSession, String userId){
return sqlSession.selectOne("userMapper.selectUserById", userId);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="userMapper">
<resultMap type="userDTO" id="userResultSet">
<!-- 언더바가 들어가는 쿼리문에 한하여 매핑을 해줘야함 -->
<id property="userId" column="user_id" />
<result property="pw" column="pw" />
<result property="name" column="name" />
<result property="phone" column="phone" />
<result property="createdDate" column="create_date" />
<result property="isDeleted" column="isdeleted" />
<result property="delDate" column="del_date" />
<collection property="authList" resultMap="authMap" />
</resultMap>
<resultMap type="AuthDTO" id="authMap">
<result property="authorityCode" column="authority_code" />
<result property="authorityName" column="authority_name" />
<result property="authority" column="authority" />
</resultMap>
<select id="selectUser" parameterType="userDTO"
resultMap="userResultSet">
SELECT *
FROM USER u left join user_authority auth on u.authority_code = auth.authority_code
WHERE user_id = #{userId}
</select>
<select id="selectUserById"
resultMap="userResultSet">
SELECT *
FROM USER u left join user_authority auth on u.authority_code = auth.authority_code
WHERE user_id = #{userId}
</select>
<select id="selectEncryptedPwd" parameterType="userDTO"
resultType="java.lang.String">
SELECT
PW
FROM USER
WHERE user_id = #{ userId }
</select>
<select id="selectTaskList" parameterType="userDTO">
SELECT *
FROM TASK
WHERE user_id = #{ userId}
</select>
<insert id="insertUser" parameterType="userDTO">
INSERT
INTO USER
(user_id,
pw, name, phone)
VALUES (#{ userId }
, #{ pw }
, #{ name }
, #{ phone })
</insert>
</mapper>
import java.sql.Date;
import java.util.List;
public class UserDTO {
private String userId;
private String pw;
private String name;
private String phone;
private Date createdDate;
private String isDeleted;
private Date delDate;
private int authorityCode;
private List<AuthDTO> authList;
public List<AuthDTO> getAuthList() {
return authList;
}
public void setAuthList(List<AuthDTO> authList) {
this.authList = authList;
}
public UserDTO() {
}
public UserDTO(String userId){
this.userId = userId;
}
public int getAuthorityCode() {
return authorityCode;
}
public void setAuthorityCode(int authorityCode) {
this.authorityCode = authorityCode;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getPw() {
return pw;
}
public void setPw(String pw) {
this.pw = pw;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Date getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
public String getIsDeleted() {
return isDeleted;
}
public void setIsDeleted(String isDeleted) {
this.isDeleted = isDeleted;
}
public Date getDelDate() {
return delDate;
}
public void setDelDate(Date delDate) {
this.delDate = delDate;
}
@Override
public String toString() {
return "UserDTO{" +
"userId='" + userId + '\'' +
", pw='" + pw + '\'' +
", name='" + name + '\'' +
", phone='" + phone + '\'' +
", createdDate=" + createdDate +
", isDeleted='" + isDeleted + '\'' +
", delDate=" + delDate +
", authorityCode=" + authorityCode +
", authList=" + authList +
'}';
}
}
public class AuthDTO {
private int authorityCode;
private String authorityName;
private String authority;
public AuthDTO() {
}
public AuthDTO(int authorityCode, String authorityName, String authority) {
this.authorityCode = authorityCode;
this.authorityName = authorityName;
this.authority = authority;
}
public int getAuthorityCode() {
return authorityCode;
}
public void setAuthorityCode(int authorityCode) {
this.authorityCode = authorityCode;
}
public String getAuthorityName() {
return authorityName;
}
public void setAuthorityName(String authorityName) {
this.authorityName = authorityName;
}
public String getAuthority() {
return authority;
}
public void setAuthority(String authority) {
this.authority = authority;
}
}
본 포스팅은 멀티캠퍼스의 멀티잇 백엔드 개발(Java)의 교육을 수강하고 작성되었습니다.