Security Login

์ด๋™์–ธยท2024๋…„ 9์›” 6์ผ

new world

๋ชฉ๋ก ๋ณด๊ธฐ
41/62
post-thumbnail

9.6(๊ธˆ)

1. Security

๐Ÿ‘‰ Sercurity์—์„œ๋Š” ์ผ๋ฐ˜์ ์ธ ๋กœ๊ทธ์ธ์ฒ˜๋Ÿผ db์—์„œ id / pw๋ฅผ ํ™•์ธํ•˜๋Š”๊ฒƒ์ด ์•„๋‹ˆ๊ณ , db์—์„œ๋Š” ์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€ password Encoder๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž์ž„์„ ํ™•์ธํ•œ๋‹ค.

2. Security๋ฅผ ์ด์šฉํ•œ ๋‹จ๊ณ„๋ณ„ Login

2-1. security-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd">

    <bean name="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>
<!--security์—์„œ๋Š” ์ผ๋ฐ˜์ ์ธ pw๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ๋˜๊ณ , passwordEncoder๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•จ.-->
    <security:http>

        <security:form-login></security:form-login>
        <security:intercept-url
                pattern="/sample/member"
                access="hasRole('ROLE_MEMBER')"/>
      <!--sample/member์˜ ๊ฒฝ๋กœ๋ฅผ ์ด๋™ํ•˜๋ ค๋ฉด, ROLE_MEMBER์˜ ๊ถŒํ•œ์„ ๊ฐ€์ ธ์•ผํ•จ-->
    </security:http>

    <security:authentication-manager>
        <security:authentication-provider> <!--์ธ์ฆ์ œ๊ณต์„ ๋ˆ„๊ฐ€ํ• ๊ฒƒ์ธ์ง€-->
            <security:user-service>
                <security:user name="user1" password="$2a$12$BVs3fK5FksdaxU.X3wbQiuucJF0KpDqRMHYJkm5cGNyry7rKQGHXa" authorities="ROLE_MEMBER"></security:user>
            </security:user-service>
        </security:authentication-provider>

    </security:authentication-manager>

</beans>

๐Ÿ‘‰ ๊ธฐ๋ณธ form-login์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด Spring Security์—์„œ ์ž๋™ ๋กœ๊ทธ์ธํŽ˜์ด์ง€๋ฅผ ์ƒ์„ฑํ•˜๋Š”๋ฐ, /login URL์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค.
๐Ÿ‘‰ userSerivce๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž ์ธ์ฆ์ •๋ณด๋ฅผ ์„ค์ •ํ•œ๋‹ค
๐Ÿ‘‰ security:user์—์„œ์˜ password๋Š” ์‚ฌ์šฉ์ž์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ •์˜ํ•˜๋Š”๋ฐ, ์ผ๋ฐ˜์ ์ธ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์•„๋‹Œ BCrypt ํ•ด์‹œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ํ†ตํ•ด ์•”ํ˜ธํ™”๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค. (https://bcrypt-generator.com/#google_vignette)
๐Ÿ‘‰ ๋˜ํ•œ user1์˜ ์ด๋ฆ„์„ ๊ฐ€์ง„ user๋Š” ROLE_MEMBER์˜ ๊ถŒํ•œ์„ ํš๋“ํ•œ๋‹ค.




2-2. web.xml (์ถ”๊ฐ€๋œ ์ฝ”๋“œ๋งŒ)

 <param-value>
            /WEB-INF/spring/root-context.xml
            /WEB-INF/spring/security-config.xml
</param-value>

 <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
   
    </filter-mapping>

๐Ÿ‘‰ url-pattern์œผ๋กœ ์ธํ•ด ๋ชจ๋“  ๊ฒฝ๋กœ์— ๋Œ€ํ•ด spring Security๋ณด์•ˆ ํ•„ํ„ฐ๊ฐ€ ์ ์šฉ๋œ๋‹ค.




3-1. CustomAccessDeniedHandler

package org.zerock.w2.security.handler;

@Log4j2
public class CustomAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {

        log.error("--------------------------");
        log.error(accessDeniedException);
        log.error(accessDeniedException.getMessage());


        response.sendError(
                HttpServletResponse.SC_FORBIDDEN,
                accessDeniedException.getMessage());
    }
}

๐Ÿ‘‰ ์ ‘๊ทผ๊ถŒํ•œ์ด ๋ถ€์กฑํ• ๋•Œ, 403์ƒํƒœ์ฝ”๋“œ์™€ ์˜ค๋ฅ˜๋ฉ”์‹œ์ง€๋ฅผ ์ „๋‹ฌ




3-2. security-config.xml (์ถ”๊ฐ€๋œ ์ฝ”๋“œ)

 <bean name="customAccessHandler" class="org.zerock.w2.security.handler.CustomAccessDeniedHandler"></bean>

 <security:intercept-url
                pattern="/sample/admin"
                access="hasRole('ROLE_ADMIN')"/>

 <security:access-denied-handler error-page="/error403"></security:access-denied-handler>

 <security:user name="admin1" password="$2a$12$Vjg7bECD6DNIgcXret3M1.FnQDcKGbPcgsyZmRlG/K91whhMsV3sG" authorities="ROLE_MEMBER,ROLE_ADMIN"></security:user>

๐Ÿ‘‰ customAccessHandler๋ฅผ ์ •์˜
๐Ÿ‘‰ sample/admin์˜ ๊ฒฝ๋กœ๋ฅผ ์ด๋™ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ROLE_ADMIN์— ๋Œ€ํ•œ ๊ถŒํ•œ์„ ์–ป์–ด์•ผํ•œ๋‹ค.
๐Ÿ‘‰ access-denied-handlerํƒœ๊ทธ๋ฅผ ํ†ตํ•ด error403ํŽ˜์ด์ง€๋กœ redirect
๐Ÿ‘‰ admin1์˜ user๋Š” ROLE_MEMBER,ROLE_ADMIN ๋‘๊ฐ€์ง€์˜ ๊ถŒํ•œ์„ ๊ฐ€์ง„๋‹ค.




3-3. Error403Controller.java

package org.zerock.w2.controller;

@Controller
@Log4j2
public class Error403Controller {

    @GetMapping("/error403")
    public void error403(Authentication auth) {
        log.info(auth); // security๋ฅผ ์ ์šฉํ•˜๋ฉด, ์‚ฌ์šฉ์ž์˜ ์ธ์ฆ์ •๋ณด๋ฅผ ์•Œ์ˆ˜์žˆ๋‹ค.
        log.error("error403...............");
    }
}

๐Ÿ‘‰ Controller๋ฅผ ํ†ตํ•ด jsp ์ด๋™




3-4. error403.jsp

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<%--
  Created by IntelliJ IDEA.
  User: USER
  Date: 2024-09-06
  Time: ์˜ค์ „ 10:42
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>Error403 Page</h1>


<sec:authorize access="isAuthenticated()">
    ์ธ์ฆ์€ ๋˜์—ˆ์œผ๋‚˜, ๊ถŒํ•œ์ด ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค. ๊ด€๋ฆฌ์ž์—๊ฒŒ ๋ฌธ์˜ํ•˜์„ธ์š”
</sec:authorize>
</body>
</html>




4-1. security-config.xml

        <security:csrf disabled="true"></security:csrf>
        <security:form-login login-page="/customLogin" login-processing-url="/login"></security:form-login>

        <security:logout></security:logout>

๐Ÿ‘‰ csrf๋Š” ๋งค๋ฒˆ ๋‹ฌ๋ผ์ง€๋Š” ํ† ํฐ๊ฐ’์„ ์ด์šฉํ•˜์—ฌ csrf๊ณต๊ฒฉ์„ ๋ฐฉ์–ดํ• ์ˆ˜์žˆ๋Š”๋ฐ, ์ด๋Ÿฌํ•œ ๊ธฐ๋Šฅ์„ ๋น„ํ™œ์„ฑํ™” ํ•˜๋Š”๊ฒƒ.
๐Ÿ‘‰ security์˜ login ํŽ˜์ด์ง€๋ฅผ customLogin์„ ๊ธฐ๋ณธ ํŽ˜์ด์ง€๋กœ ์„ค์ •ํ•˜๊ณ , login-processing-url์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž์˜ ์ด๋ฆ„๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ œ์ถœ๋  url์„ ์ง€์ •(jsp์—์„œ์˜ form action๊ฒฝ๋กœ)
๐Ÿ‘‰ logout์— ๋Œ€ํ•œ ๊ธฐ๋Šฅ ์ถ”๊ฐ€




4-2. CustomLoginController.java

package org.zerock.w2.controller;

@Controller
@Log4j2
public class CustomLoginController {

    @GetMapping("/customLogin")
    public void login(String error, Model model) {
        log.info("login");

        if(error != null) {
            model.addAttribute("errorMsg", "Login failed");
        }

    }

}

๐Ÿ‘‰ /customLogin ์œผ๋กœ ์ด๋™ํ•˜๊ฒŒ๋˜๋ฉด ํ•ด๋‹น customLogin.jsp ๋กœ ์ด๋™ํ•˜๊ฒŒ ๋˜๊ณ , error๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด jsp๋กœ errormsg๋ฅผ ์ „๋‹ฌํ•˜๊ฒŒ๋จ




4-3. customLogin.jsp

<html>
<head>
    <title>Title</title>
</head>
<body>

<h1>Custom Login Page</h1>

<form method="post" action="/login">
    <div>
        <input type="text" name="username">
    </div>
    <div>
        <input type="password" name="password">
    </div>
    <div>
        <button>LOGIN</button>
    </div>
</form>

</body>
</html>

๐Ÿ‘‰ form์˜ post๋Š” security-config.java๋‚ด๋ถ€์˜ login-processing-url="/login" ์—์„œ ์ˆ˜์‹ ํ•˜์—ฌ ์ฒ˜๋ฆฌํ•œ๋‹ค.




5-1. CustomUserDetailsService.java

package org.zerock.w2.security;



@Log4j2
@Service(value = "customDetails")
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {

    private final TimeMapper timeMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        log.info("----------------");
        log.info("----------------");
        log.info("loadUserByUsername: " + username);
        log.info(timeMapper.getTime());
        log.info("----------------");
        log.info("----------------");

        return null;
    }
}

๐Ÿ‘‰ ํ•ด๋‹น CustomUserDetailsService ๋ฉ”์†Œ๋“œ๋Š” UserDetialService์˜ ์ƒ์†๊ด€๊ณ„ํ•˜์—ฌ loadUserByUsername ๋ฉ”์†Œ๋“œ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œ ํ•˜๊ณ ์žˆ๋‹ค.
๐Ÿ‘‰ UserDetails ๋ฐ์ดํ„ฐํƒ€์ž…์„ ๋ฐ˜ํ™˜ํ•˜๊ณ , username์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›๋Š”๋‹ค.




5-2. security-config.xml

 xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

 <context:component-scan base-package="org.zerock.w2.security"></context:component-scan>

  <security:authentication-provider user-service-ref="customDetails"> <!--์ธ์ฆ์ œ๊ณต์„ ๋ˆ„๊ฐ€ํ• ๊ฒƒ์ธ์ง€-->

๐Ÿ‘‰ security:authentication-provider์€ ์‚ฌ์šฉ์ž ์ธ์ฆ์„ ์ฒ˜๋ฆฌํ•  UserDetailsService๋ฅผ ์ง€์ •ํ•˜๋Š” ์—ญํ• ๋กœ, ์ง€์ •๋œ CustomUserDetailsService.java๋Š” ์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋ฅผ ์ €์กฐํšŒํ•˜์—ฌ UserDetails๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.




6-1. MemberMapper.java

package org.zerock.w2.mapper;

import org.apache.ibatis.annotations.Param;
import org.zerock.w2.vo.MemberVO;

public interface MemberMapper {

    void insert(MemberVO memberVO);

    void insertRole(@Param("mid") String mid, @Param("role") String role);
}




6-2. MemberVO.java

package org.zerock.w2.vo;

import lombok.Data;

@Data
public class MemberVO {

    private String mid;
    private String mpw;
    private String mname;
}




6-3. MemberMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.zerock.w2.mapper.MemberMapper">

    <insert id="insert">
        insert into tbl_member (mid, mpw,mname)
        values (#{mid}, #{mpw}, #{mname})
    </insert>

    <insert id="insertRole">
        insert into tbl_member_role (mid, role)
        values (#{mid}, #{role})
    </insert>

</mapper>




6-4. MemberMapperTests.java

package org.zerock.w2.mapper;

@ExtendWith(SpringExtension.class)
@ContextConfiguration({
        "file:src/main/webapp/WEB-INF/spring/root-context.xml",
        "file:src/main/webapp/WEB-INF/spring/security-config.xml"
})
@Log4j2
public class MemberMapperTests {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired(required = false) // MemberMapper์— ๋Œ€ํ•ด์„œ๋Š” bean์ด ์—†๊ธฐ๋•Œ๋ฌธ
    private MemberMapper memberMapper;

    @Test
    public void test1() {

        String pw = "1111";
        for(int i = 1; i <= 100; i++){

            String encodedPw = passwordEncoder.encode(pw); // 1111์„ ํŒจ์Šค์›Œ๋“œ ์ธ์ฝ”๋”๋ฅผ ์ด์šฉํ•ด์„œ ํ•ด์‹œ์•”ํ˜ธํ™”
            String mid = "user"+i;
            String mname = "USER"+i;

            List<String> roles = new ArrayList<>();
            roles.add("USER"); // ๋ชจ๋“ ์‚ฌ์šฉ์ž๊ฐ€ user์˜ ๊ถŒํ•œ์„ ๊ฐ–๋Š”๋‹ค

            if(i>=60){
                roles.add("MANAGER"); // 60์ด์ƒ์€ MANAGER ๊ถŒํ•œ๋„ ๊ฐ–๋Š”๋‹ค
            }
            if(i>=80){
                roles.add("ADMIN"); // 80์ด์ƒ์€ ADMIN๊นŒ์ง€ 3๊ฐœ์˜ ๊ถŒํ•œ์„ ๊ฐ–๋Š”๋‹ค.
            }

            MemberVO memberVO = new MemberVO();
            memberVO.setMid(mid);
            memberVO.setMpw(encodedPw);
            memberVO.setMname(mname);

            memberMapper.insert(memberVO);

            roles.forEach(role -> {
                memberMapper.insertRole(mid,role);
            });

        }


    }
}

๐Ÿ‘‰ passwordEncoder๋ฅผ ์ฃผ์ž…ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ณ , memberMapper๋ฅผ ์ฃผ์ž…ํ•˜๋Š”๋ฐ, MemberMapper๋Š” bean์ด ์—†๋”๋ผ๋„ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๋„๋ก required = false
๐Ÿ‘‰ 1111์ด๋˜ pw๊ฐ€ passwordEncoder๋ฅผ ํ†ตํ•ด ํ•ด์‹œ์•”ํ˜ธํ™”
๐Ÿ‘‰ roles(user, manager, admin) 3๊ฐ€์ง€๋กœ ๋ถ„๋ฆฌ๋˜๋ฏ€๋กœ i๊ฐ€ 80์ด์ƒ์ผ๋•Œ 3๊ฐœ์˜ ๊ถŒํ•œ์„ ๊ฐ€์งˆ์ˆ˜์žˆ๊ธฐ๋•Œ๋ฌธ์— roles๋Š” List๋กœ ์ฒ˜๋ฆฌ
๐Ÿ‘‰ memberVO์— ๋งž๊ฒŒ mid, pw, mname์„ for๋‚ด๋ถ€์—์„œ 100๊ฐœ์˜ memberVO๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด์„œ memberMapper์˜ insert๋‚ด๋ถ€์— ๋„ฃ์–ด db์— ์ €์žฅ
๐Ÿ‘‰ roles๋Š” 3๊ฐ€์ง€์˜ ์—ญํ• ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— forEach๋ฌธ์„ ํ†ตํ•ด์„œ ํ•˜๋‚˜์˜ mid์— ๊ฐ๊ฐ์˜ role์„ ๋ถ€์—ฌํ•œ๋‹ค (์˜ˆ : user1 - USER , user1 - MANAGER)




7-1. MemberDTO.java

package org.zerock.w2.dto;

import lombok.Data;

import java.util.List;

@Data
public class MemberDTO {

    private String mid;
    private String mpw;
    private String mname;

    private List<String> roles; // 1๊ฐœ์˜ mid๋‹น ์—ฌ๋Ÿฌ๊ฐœ์˜ role์„ ๊ฐ€์งˆ์ˆ˜์žˆ์œผ๋ฏ€๋กœ
}




7-2. MemberMapper.java

package org.zerock.w2.mapper;

public interface MemberMapper {

    void insert(MemberVO memberVO);

    void insertRole(@Param("mid") String mid, @Param("role") String role);

    MemberDTO getMember(@Param("mid") String mid);

}




7-3. MemberMapper.xml

 <resultMap id="readMap" type="MemberDTO">
        <id property="mid" column="mid"></id>
        <result property="mpw" column="mpw"></result>
        <result property="mname" column="mname"></result>
        <collection property="roles" resultMap="rolesMap"></collection>
    </resultMap>

    <resultMap id="rolesMap" type="string">
        <result column="role"></result>
    </resultMap>

    <select id="getMember" resultMap="readMap">
        select m.mid, mpw, mname, role
        from
        tbl_member m inner join tbl_member_role r on r.mid = m.mid
        where
        m.mid = #{mid}
    </select>

๐Ÿ‘‰ ์—ฌ๊ธฐ์„œ resultMap์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š”, MemberDTO์—์„œ roles๊ฐ€ List์ด๋ฏ€๋กœ, ๊ฒฐ๊ตญ ํ•˜๋‚˜์˜ mid์— ์—ฌ๋Ÿฌ๊ฐœ์˜ role์ด ์กด์žฌํ•˜๋Š”๊ฒƒ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์„ ํ•œ์ค„์˜ ํ–‰์œผ๋กœ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ๋Š” resultMap์„ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.
๐Ÿ‘‰ select๋ฅผ ํ†ตํ•ด์„œ tbl_member์™€ tbl_member_role์„ inner joinํ•˜๊ณ , resultMap์„ ์‚ฌ์šฉํ•œ๋‹ค.
๐Ÿ‘‰ resultMap์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ฒซ๋ฒˆ์งธ๋กœ ๊ธฐ์กด sql์ฝ”๋“œ์— resultMap์„ ์„ ์–ธํ•˜๊ณ  ์„ ์–ธ๋  resultMap์˜ id๋ฅผ ์ž‘์„ฑ (select id="getMember" resultMap="readMap")
๐Ÿ‘‰ resultMap์—์„œ id๋Š” pk๊ฐ’์œผ๋กœ mid๊ฐ€ ๋“ค์–ด๊ฐ€๊ณ , result๋Š” ์ผ๋ฐ˜์ ์ธ ๊ฐ’์ด ๋“ค์–ด๊ฐ€๊ฒŒ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  list์™€ ๊ฐ™์€ ๊ฐ’์€ collection์œผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.




7-4. MemberMapperTest.java

    @Test
    public void testGetMember(){
        String mid = "user90";

        MemberDTO memberDTO = memberMapper.getMember(mid);
        log.info(memberDTO);

๐Ÿ‘‰ ํ•ด๋‹น์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ฒŒ ๋˜๋ฉด ์•„๋ž˜๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค.
MemberDTO(mid=user90, mpw=2a$10b.QoIBmrVpe590.rlzzHmOvL7qc.bLvYOjCwg1LVRjlxjqQ2W6b.K, mname=USER90, roles=[USER, MANAGER, ADMIN])




8-1. MemberDTO.java

public class MemberDTO implements UserDetails {

    private String mid;
    private String mpw;
    private String mname;

    private List<String> roles; // 1๊ฐœ์˜ mid๋‹น ์—ฌ๋Ÿฌ๊ฐœ์˜ role์„ ๊ฐ€์งˆ์ˆ˜์žˆ์œผ๋ฏ€๋กœ

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return roles.stream().map(role -> new SimpleGrantedAuthority("ROLE_"+role)).collect(Collectors.toList());
    } // ๊ทธ๋Ÿฌ๋ฉด ROLE_user , ROLE_manager, ROLE_admin ์ด๋ ‡๊ฒŒ ๋งŒ๋“ค์–ด์ง„๋‹ค.

    @Override
    public String getPassword() { //pw๊ฐ€ ๋ญ์•ผ
        return mpw;
    }

    @Override
    public String getUsername() { // id๊ฐ€ ๋ญ์•ผ?
        return mid;
    }

    @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-2. CustomUserDetailService

    private final MemberMapper  memberMapper;
    
    
        MemberDTO memberDTO = memberMapper.getMember(username);
        
        return memberDTO;

๐Ÿ‘‰ ๊ฒฐ๊ตญ memberDTO๋ฅผ UserDetails์˜ ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™˜ํ•ด์ฃผ๋Š”๊ฒƒ์ธ๋ฐ, ๊ทธ๋Ÿฌ๋ฉด DTO๋ฅผ UserDetail์˜ ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๊ธฐ์œ„ํ•ด์„œ 8-1์—์„œ UserDetails๋ฅผ ์ƒ์†ํ•œ๊ฒƒ์ด๋‹ค.




9-1. security-config.xml

<security:remember-me data-source-ref="dataSource" token-validity-seconds="604800"></security:remember-me>

๐Ÿ‘‰ remember-me๋ฅผ ์ด์šฉํ•˜์—ฌ ์„ธ์…˜์ฒ˜๋Ÿผ ๊ธฐ์–ตํ•˜์—ฌ ์ž๋™๋กœ๊ทธ์ธ ์ด์šฉ๊ฐ€๋Šฅ




9-2. customLogin.jsp

<div>
     <input type="checkbox" name="remember-me">์ž๋™๋กœ๊ทธ์ธ
</div>




10-1. BoardController.java

package org.zerock.w2.controller;

@Controller
@RequestMapping("/board/") // ์ฃผ์†Œ๊ฐ€ board๋กœ ์‹œ์ž‘
@Log4j2
@RequiredArgsConstructor
public class BoardController {

    private final BoardService boardService;

    @GetMapping("register")
    public void register(Authentication authentication, Model model) {

        log.info("register");

        MemberDTO memberDTO = (MemberDTO) authentication.getPrincipal();



        log.info(memberDTO);
        // ํ˜„์žฌ์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜, authentication์€ ์‚ฌ์šฉ์ž์˜ ์ •๋ณด์ œ๊ณต, getPrincipal์€ ์ธ์ฆ๋œ
    }

    @PreAuthorize("principal.username == #dto.writer")
    @PostMapping("register")
    public String register(BoardRegisterDTO dto, RedirectAttributes rttr) {

        log.info(dto);

        boardService.register(dto);

        return "redirect:/board/list";
    }

    @GetMapping("list")
    public void list(PageRequest pageRequest, Model model){ //PageRequest ๊ฐ์ฒด๋ฅผ pg๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ์‚ฌ์šฉ
        // modelAttribute๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ’์„ pg์— ๋ฐ”์ธ๋”ฉํ•œ๋‹ค, page=1 , size=10๊ณผ ๊ฐ™์ด
        log.info("list");
        model.addAttribute("result",boardService.getList(pageRequest));

    }

    @PreAuthorize("isAuthenticated()") // ๋กœ๊ทธ์ธ์ด ๋œ ์‚ฌ์šฉ์ž๋งŒ ์ด์šฉํ• ์ˆ˜ ์žˆ๋„๋ก
    @GetMapping("read/{bno}")
    public String read(@PathVariable("bno") Long bno, Model model){ // ์ฃผ์†Œ์ฐฝ์—์„œ (read/12) ์ฒ˜๋Ÿผ ๋˜๊ธฐ ์œ„ํ•ด

        model.addAttribute("board",boardService.getOne(bno));

        return "board/read";
    }
}

๐Ÿ‘‰ @PreAuthorize("principal.username == #dto.writer") ํ•ด๋‹น ์–ด๋…ธํ…Œ์ด์…˜์€ ํ˜„์žฌ ์‚ฌ์šฉ์ค‘์ธ ์‚ฌ์šฉ์ž์ด๋ฆ„๊ณผ register page์—์„œ ๋“ฑ๋กํ• ๋•Œ ํ•„์š”ํ•œ ์†์„ฑ์ค‘ writer์†์„ฑ๊ณผ ์ผ์น˜ํ• ๋•Œ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ํ•˜๊ณ 
๐Ÿ‘‰ @PreAuthorize("isAuthenticated()") ํ•ด๋‹น ์–ด๋…ธํ…Œ์ด์…˜์€ ๋กœ๊ทธ์ธ์ด ๋œ ์‚ฌ์šฉ์ž๋งŒ ์ด์šฉํ• ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•œ๋‹ค.




10-2. SampleController.java

package org.zerock.w2.controller;

@Controller
@RequestMapping("/sample/")
public class SampleController {

    @GetMapping("all")
    public void all(){

    }

    @PreAuthorize("hasRole('ROLE_MANAGER')")
    @GetMapping("member")
    public void member(){

    }

    @GetMapping("admin")
    public void admin(){

    }

}

๐Ÿ‘‰ @PreAuthorize("hasRole('ROLE_MANAGER')") ํ•ด๋‹น ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ROLE_MANAGER์˜ ๊ถŒํ•œ์„ ๊ฐ–๊ณ ์žˆ๋Š” ์‚ฌ์šฉ์ž๋งŒ ์ ‘๊ทผํ• ์ˆ˜์žˆ๋„๋ก ํ•˜๊ณ 




10-3. security-config.xml

        <security:intercept-url
                pattern="/sample/member"
                access="hasRole('ROLE_MANAGER')"/> <!--ROLE_ ๋Š” ํ‚ค์›Œ๋“œ ์ ‘๋‘์‚ฌ๋กœ ๋ณ€๊ฒฝ X-->

        <security:intercept-url
                pattern="/sample/admin"
                access="hasRole('ROLE_ADMIN')"/>

๐Ÿ‘‰ ๊ธฐ์กด์— ์‚ฌ์šฉํ•˜๋˜ xml๋‚ด๋ถ€์˜ security:interceptํƒœ๊ทธ๋Š” ์ œ๊ฑฐ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.




10-4. servlet-context.xml

xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd">
       
<security:global-method-security pre-post-annotations="enabled" secured-annotations="enabled">
    </security:global-method-security>

๐Ÿ‘‰ ๋‘๊ฐ€์ง€์˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ, @PreAuthorize, @PostAuthorize ์™€ ๊ฐ™์€ ์–ด๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ์„ ํ™œ์„ฑํ™” ์‹œํ‚จ๋‹ค.




11. DB์„ค๊ณ„

create table tbl_member (
                            mid varchar(100) not null,
                            mpw varchar(100) not null,
                            mname varchar(100) not null
);

alter table tbl_member add constraint pk_member primary key (mid);

create table tbl_member_role (
                                 mid varchar(100) not null,
                                 role varchar(50) not null
);

alter table tbl_member_role
    add constraint fk_member foreign key (mid)
        references tbl_member(mid)
;


CREATE TABLE persistent_logins (
                                   username VARCHAR(64) NOT NULL, -- ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž์˜ ์ด๋ฆ„
                                   series VARCHAR(64) PRIMARY KEY, -- ํ† ํฐ์˜ ์‹œ๋ฆฌ์ฆˆ (๊ณ ์œ  ์‹๋ณ„์ž)
                                   token VARCHAR(64) NOT NULL, -- ์‹ค์ œ ํ† ํฐ ๊ฐ’
                                   last_used TIMESTAMP NOT NULL -- ๋งˆ์ง€๋ง‰์œผ๋กœ ์‚ฌ์šฉ๋œ ์‹œ์ 
);

0๊ฐœ์˜ ๋Œ“๊ธ€