
๐ Sercurity์์๋ ์ผ๋ฐ์ ์ธ ๋ก๊ทธ์ธ์ฒ๋ผ db์์ id / pw๋ฅผ ํ์ธํ๋๊ฒ์ด ์๋๊ณ , db์์๋ ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ password Encoder๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์์์ ํ์ธํ๋ค.
<?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์ ๊ถํ์ ํ๋ํ๋ค.
<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๋ณด์ ํํฐ๊ฐ ์ ์ฉ๋๋ค.
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์ํ์ฝ๋์ ์ค๋ฅ๋ฉ์์ง๋ฅผ ์ ๋ฌ
<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 ๋๊ฐ์ง์ ๊ถํ์ ๊ฐ์ง๋ค.
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 ์ด๋
<%@ 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>
<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์ ๋ํ ๊ธฐ๋ฅ ์ถ๊ฐ
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๋ฅผ ์ ๋ฌํ๊ฒ๋จ
<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" ์์ ์์ ํ์ฌ ์ฒ๋ฆฌํ๋ค.
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์ ๋งค๊ฐ๋ณ์๋ก ๋ฐ๋๋ค.
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๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ ์ญํ ์ ํ๋ค.
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);
}
package org.zerock.w2.vo;
import lombok.Data;
@Data
public class MemberVO {
private String mid;
private String mpw;
private String mname;
}
<?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>
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)
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์ ๊ฐ์ง์์์ผ๋ฏ๋ก
}
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);
}
<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์ผ๋ก ์ฌ์ฉ๋๋ค.
@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])
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;
}
private final MemberMapper memberMapper;
MemberDTO memberDTO = memberMapper.getMember(username);
return memberDTO;
๐ ๊ฒฐ๊ตญ memberDTO๋ฅผ UserDetails์ ํํ๋ก ๋ฐํํด์ฃผ๋๊ฒ์ธ๋ฐ, ๊ทธ๋ฌ๋ฉด DTO๋ฅผ UserDetail์ ํํ๋ก ๋ณํํด์ฃผ๊ธฐ์ํด์ 8-1์์ UserDetails๋ฅผ ์์ํ๊ฒ์ด๋ค.
<security:remember-me data-source-ref="dataSource" token-validity-seconds="604800"></security:remember-me>
๐ remember-me๋ฅผ ์ด์ฉํ์ฌ ์ธ์ ์ฒ๋ผ ๊ธฐ์ตํ์ฌ ์๋๋ก๊ทธ์ธ ์ด์ฉ๊ฐ๋ฅ
<div>
<input type="checkbox" name="remember-me">์๋๋ก๊ทธ์ธ
</div>
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()") ํด๋น ์ด๋
ธํ
์ด์
์ ๋ก๊ทธ์ธ์ด ๋ ์ฌ์ฉ์๋ง ์ด์ฉํ ์ ์๋๋ก ์ง์ํ๋ค.
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์ ๊ถํ์ ๊ฐ๊ณ ์๋ ์ฌ์ฉ์๋ง ์ ๊ทผํ ์์๋๋ก ํ๊ณ
<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ํ๊ทธ๋ ์ ๊ฑฐ๊ฐ ๊ฐ๋ฅํ๋ค.
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 ์ ๊ฐ์ ์ด๋ ธํ ์ด์ ์ฌ์ฉ์ ํ์ฑํ ์ํจ๋ค.
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 -- ๋ง์ง๋ง์ผ๋ก ์ฌ์ฉ๋ ์์
);