회원가입 암호화.
1. pom.xml에 버전넣기(초기에 넣어져있음) + 4가지 라이브러리
<properties>
<org.springframework.security-version>4.2.11.RELEASE</org.springframework.security-version>
</properties>
<!-- Security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${org.springframework.security-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${org.springframework.security-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${org.springframework.security-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${org.springframework.security-version}</version>
</dependency>
2. web.xml에서 Spring Security관련 설정하기
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml
/WEB-INF/spring/security-context.xml</param-value>
</context-param>
Spring 전용 필터 삽입을 위해 Spring security Api검색하고 복붙하기





//web.xml
<!-- spring security 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>
3. web.xml에서 설정해준 security-context.xml의 이름대로 파일 생성하기.

그 다음 파일안에 다음과 같은 내용 셋팅하기.( root-context.xml의 beans부분 복붙해서 필요에 맞게 수정.)
<beans> 나머지 셋팅 </beans>을 할 예정이다.
//security-context.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:context="http://www.springframework.org/schema/context"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd">
</beans>
4. security-context.xml 설정
- 일단 도메인인 com.myweb을 읽어오겠다고 선언하고
bcryptPwdEncoder라는 security가 기본적으로 가지고 있는 클래스를 사용한다고 선언하다.
//security-context.xml
<context:component-scan base-package="com.myweb"/>
<bean id="bcryptPwdEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>
BCryptPasswordEncoder는 비번을 암호화 해주는것과 로그인시 입력하는 비번을 암호화 비번과 비교해 주는 메서드가 존재한다.
(Maven에 security-core에서 확인가능하다.)

- intercept-url은 url주소를 가로챈다는 것으로,
jsp에서 url을 보낼 때 아래 설정한 url로 보내게 되면 Ctrl로 가지 않고 Security가 가로채서 가져가 처리한다.
그리고 그 결과 값을 Ctrl로 전달한다.
원래 주인으로 중심역할을 했던 Ctrl이 하인으로 전락하는 상황인 것이다.
access는 접속권한이 높을수록 먼저 작성해야 한다.
반대로 접속권한이 낮은 순으로 셋팅하면 그 아래로 접속권한을 높게 해도 모든 사람에게 access된다.
//security-context.xml
<security:http auto-config="true" use-expressions="true">
<security:intercept-url pattern="/admin/**" access="hasRole('ROLE_ADM')"/>
<security:intercept-url pattern="/product/**" access="hasAnyRole('ROLE_MEM','ROLE_ADM')"/>
<security:intercept-url pattern="/**" access="permitAll()"/>
</security:http>
- 로그인/회원가입의 컬럼요소 등록하기
내가 로그인 form에서 사용한 name과 같은 이름으로 username과 password 파라미터 설정하기.
로그인이 일어나는 페이지와 로그인후 타겟 페이지(main)
<security:form-login username-parameter="email" password-parameter="pwd"
login-processing-url="/member/login" login-page="/member/login"
default-target-url="/"/>
- 세션에 같이 아이디가 2개 들어오지 못하게 설정. (한 아이디로 접속할 수 있는 허용수. 1로 설정함.)
2개들어왔을때 "/" 로그아웃 시키고 main페이지로 돌려보냄.
<security:session-management>
<security:concurrency-control max-sessions="1" expired-url="/"/>
</security:session-management>
5. DBeaver에서 tbl_memeber 수정.
로그인에 security를 적용하기 위해서 컬럼으로 auth, enabled, failcnt 추가한다.
그리고 전에 추가 했던 회원데이터를 모두 제거 한다.
alter table tbl_member add (auth varchar2(10) default 'ROLE_MEM');
alter table tbl_member add (enabled number(1,0) default 1);
alter table tbl_member add (failcnt number(1,0) default 0);
TRUNCATE TABLE TBL_MEMBER ;
6. 암호화
get방식을 제외한 모든 방법은 token값이 필요하다.
암호화를 하기위해서 bean에 id="bcryptPwdEncoder"등록했기때문에
MemebrCtrl에서
@Inject BCryptPasswordEncoder bcPwdEnc; 받을 수 있다.
이제 패스워드의 암호화를 위해 MemberCtrl에 아래의 내용을 추가한다.
//MemberCtrl.java
@PostMapping("/join")
public String join(MemberVO mvo) {
String encPwd = bcPwdEnc.encode(mvo.getPwd());
mvo.setPwd(encPwd);
}
패스워드 인코더를 사용한다고 선언해주기
//security-context.xml
<security:authentication-manager>
<security:authentication-provider>
<security:password-encoder ref="bcryptPwdEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
7. login.jsp
이제 httpSession을 쓰지 않는다. 따라서 지워준다.
post로 요청하면 security에서 인터셉트해서 파라미터를 판단하고
security에 해당하는 클래스를 다 돌리고 해당하는 결과값을 다시 돌려줌.
이 로직처리를 Ctrl에서 하지 않고 따로 처리해주기 때문에 이 부분도 빌드 해야한다.
아예 비번이 인터셉팅 당해서 Ctrl로 오지 않고 따로 빠진다.
Ctrl에서는 신호에 대한것을 받고 재전송해주는 역할을 하게 되고 로직처리는 따로 Security쪽으로 만들어서.
즉, 이제부터 Security 자체적으로 처리하게 된다.
주인이었던 Ctrl은 Security의 종이된다.
@PostMapping("/login")
public String login() {
return "redirect:/member/login";
}
Security가 처리했다고 치고
Ctrl에서 결과값을 받는것은 HttpServletReqes로 받는다.
@PostMapping("/login")
public String login(HttpServletRequest req, RedirectAttributes reAttr) {
reAttr.addFlashAttribute("eamil",req.getAttribute("eamil"));
reAttr.addFlashAttribute("err_msg", req.getAttribute("err_msg"));
return "redirect:/member/login";
}
여기서 redirect는 get방식이 기본이므로 위 메소드 후@GetMapping("/login")으로 이동한다.
join.jsp와 login.jsp에서 폼태그 아래에 토큰 추가
csrf는 security에 내장된 기능으로 token값을 사용할 수 있게 해준다.
<input type="hidden" name="${_csrf.parameterName }" value="${_csrf.token }">
login.jsp에 에러 메세지 만들어주기
<c:if test="${not empty err_msg }">
<div class="form-group">
<p class="test-danger"> 다음과 같은 이유로 로그인을 실패하였습니다.:
<c:choose>
<c:when test="">
<c:set var="errMsg" value="이메일 혹은 비밀번호가 정확하지 않습니다." />
</c:when>
<c:when test="">
<c:set var="errMsg" value="계정이 비활성화 되어 있습니다." />
</c:when>
<c:otherwise>
<c:set var="errMsg" value="관리자에게 문의하세요" />
</c:otherwise>
</c:choose>
</p>
</div>
</c:if>
그리고 email과 pwd에 security에서 설정한 value값과 같은 값을 받아오기.
<div class="form-group">
<label for="email">Email: </label> <input type="email"
class="form-control" placeholder="Enter email" name="email" value="${email }">
</div>
<div class="form-group">
<label for="pwd">Password:</label> <input type="password"
class="form-control" id="pwd" placeholder="Enter password"
name="pwd" value="${pwd }">
</div>
8. Security의 Service단 생성
security-context에서 서비스 부분 셋팅
<bean id="userService" class="com.myweb.service.AuthMemberDetailService"></bean>
<security:authentication-manager>
<security:authentication-provider user-service-ref="userService">
<security:password-encoder ref="bcryptPwdEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
</beans>
security-context에서 설정한 이름대로 AuthMemberDetailService.java클래스 생성.
email만 받아올것이므로
메소드를 다시 생성해줘야한다.
public class AuthMemberDetailService implements UserDetailsService{
private static Logger log = LoggerFactory.getLogger(AuthMemberDetailService.class);
@Inject
private MemberDAO mdao;
//username을 email로 여기의 username은 security-context에 username-parameter="email" 과같다.
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
MemberVO mvo = mdao.selectMember(email);
return null;
}
}
DAO에 메소드 생성
//MemberDAO.java
public MemberVO selectMember(String email);
//
@Override
public MemberVO selectMember(String email) {
return sql.selectOne(ns+"login", email);
}
MemberMapper에서
<select id="login" parameterType="java.lang.String" resultType="MemberVO">
select * <include refid="base"/>
</select>
다시 AuthMemberDetailService.java로 돌아와서 email을 가지고 있는 mvo를 받아오는데 error가 생긴다.!!
바로 return에서 error가 나는데 return type이 우리가 지정한 UserDetails와 결국 반환한 값인 mvo가 다르기때문에
public class AuthMemberDetailService implements UserDetailsService{
private static Logger log = LoggerFactory.getLogger(AuthMemberDetailService.class);
@Inject
private MemberDAO mdao;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
MemberVO mvo = mdao.selectMember(email);
if(mvo == null) {
throw new UsernameNotFoundException(email);
}
return mvo;
}
}
해결방법은 MemberVO 에서 UserDetails을mvo객체에 implements한다.
그리고 아래 변수들을 추가하고 Add inimplemented moethod를 추가한다.
public class MemberVO implements UserDetails{
private String auth; //Role
private boolean enabled; //활성화/비활성화
private int failcnt;//실패횟수
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
ArrayList<GrantedAuthority> auths= new ArrayList<>();
auths.add(new SimpleGrantedAuthority(auth));
return auths;
}
@Override
public String getPassword() {
return getPwd();
}
@Override
public String getUsername() {
return getEmail();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return this.enabled;
}
//나머지는 getter만 필요함
public int getFailcnt() {
return failcnt;
}
public void setFailcnt(int failcnt) {
this.failcnt = failcnt;
}
}
이 때, Security가 돌아갈때는 MemberVO가 항상 메모리에 상주하고 있다고 부여하는 아래와 같은 것드리 있어야하나
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 8216563413158534863L;
없어도 무시하라는 의미로 @SuppressWarnings("serial")를 부여한다.
후에 security-context.xml에
access="hasRole('ROLE_ADM')", access="hasRole('ROLE_MEM','ROLE_ADM')", access="permitAll()"과
연결되는 getAuthorities()부분을 빌드해준다.
9. 로그인시 화면단에 표시되도록 하기. 그리고 로그아웃하기.
- 로그인
nav.jsp에 session에 대한 부분들 삭제하고 다시 빌드
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
<!-- 로그인안한사용자 = 익명의 사용자-->
<sec:authorize access="isAnonymous()">
<li class="nav-item"><a class="nav-link" href="/member/join">회원가입</a></li>
<li class="nav-item"><a class="nav-link" href="/member/login">로그인</a></li>
</sec:authorize>
<li class="nav-item"><a class="nav-link"
href='/member/detail?email=<sec:authentication property="principal.email"/>'>
<sec:authentication property="principal.email" />
</a> </li>
<!-- 로그인한사용자 = 인증받은 사용자 -->
<sec:authorize access="isAuthenticated()">
<li class="nav-item"><a class="nav-link" href="/member/list">회원목록</a></li>
<li class="nav-item"><a class="nav-link" href="#" id="logout">로그아웃</a></li>
</sec:authorize>
-로그아웃
로그아웃 클릭시 이동하지 않고 post로 전송만되게 셋팅.
//nav.jsp
<form action="/member/logout" method="post" id="logoutForm">
<input type="hidden" name="${_csrf.parameterName }" value="${_csrf.token }">
</form>
//nav.jsp
<script>
$(function(){
$("#logout").on("click", function(e){
e.preventDefault();
$("#logoutForm").submit();
});
});
</script>
security-context에서 logout관련하여 셋팅해준다.
//security-context.xml
<security:logout logout-url="/member/logout" invalidate-session="true" logout-success-url="/"/>
원래는 nav.jsp를 거쳐 Ctrl로 이동하나 중간에 security가 "/member/logout"의 주소로 보낸 post 방식의 전송을
인터셉트 해서 가져가서 처리한다.
따라서 Ctrl단의 GetMapping부분이 필요없게 된다.
//MemberCtrl.java 주석처리하기
/*
* @GetMapping("/logout") public String logout(HttpSession ses) {
* ses.invalidate(); return "redirect:/"; }
*/
//security-context.xml
<security:form-login username-parameter="email" password-parameter="pwd"
login-processing-url="/member/login" login-page="/member/login"
default-target-url="/"
authentication-failure-handler-ref="loginFailHandler"/>
10. 로그인 5번 실패시 계정 잠기게 하기
loginFailHandler의 이름으로 실패시 핸들링 해주는 핸들러를 만든다고 선언한다.
//security-context.xml
<security:form-login username-parameter="email" password-parameter="pwd"
login-processing-url="/member/login" login-page="/member/login"
default-target-url="/"
authentication-failure-handler-ref="loginFailHandler"/>
//아래쪽에
<bean id="loginFailHandler" class="com.myweb.handler.LoginFailHandler">
<property name="authEmail" value="email"></property>
<property name="authPwd" value="pwd"></property>
<property name="errMsg" value="err_msg"></property>
<property name="failUrl" value="/member/login?error"></property>
</bean>
실제 src/main/java/com/myweb에 handler 패키지를 만들어 LoginFailHandler의 이름으로 클래스를 생성해준다.


LoginFailHandler.java에서 어떤 execption에서 파생됐는지 알 수 있게 해준다.
여기서 실패카운트인 failcnt의 갯수를 올려주는 메소드를 loginFailCountUp이라는 이름으로 따로 생성하기 위해 여기선 불러만 온다.
//LoginFailHandle.java
if(exception instanceof BadCredentialsException || exception instanceof InternalAuthenticationServiceException) {
loginFailCountUp(email);
}
loginFailCountUp메소드 생성하는데 MemberVO에 있는 failcnt의 갯수를 올리기 위해서는
DB에 update 될 수 있게 Mapper에 sql문을 작성해야하므로
Service-->DAO순으로 따라서 메소드를 생성해줘야한다.
//LoginFailCountUp.java
private void loginFailCountUp(String email) {
msv.upFailCount(email);
}
먼저 Service를 불러오기 위해서 위쪽에 @Inject MemberService msv;을 해주고
upFailCount라는 이름으로 Service로 보내는데
이 때 해당 email에 대한 실패 값이므로 email을 실어 보낸다.
//LoginFailHandler.java
private void loginFailCountUp(String email) {
msv.upFailCount(email);
}
upFailCount부분에 빨간줄이 뙇!! 되는데 service에서 불러온다고 한 메소드인 upFailCount가 아직 실제로 존재 하지 않기때문이다.
---> service단에 upFailCount메소드를 생성해준다.
//MemberService.java
public void upFailCount(String email);
//MemberServiceImpl.java
@Override
public void upFailCount(String email) {
mdao.updateFailCount(email);
}
이제 mdao.updateFailCount에도 빨간줄이 뙇!
---> DAO에도 마찬가지로 메소드를 생성해준다.
//MemberDAO.java
public void updateFailCount(String email);
//MemberDAOImpl
@Override
public void updateFailCount(String email) {
sql.update(ns+"upFailCount", email);
}
마지막으로 Mapper에 upFailCount라는 이름으로 update문을 작성한다.
<update id="upFailCount" parameterType="java.lang.String">
update tbl_member set failcnt = failcnt + 1
where email = #{email}
</update>
이제 failcnt를 올리는 작업은 끝났고 다음으로 할 것이 5번 실패했을때 계정이 잠기게 하는것이다.
잠기게 하기 위해서는 일단 failcnt값을 불러와야하고 불러온 값이 5번이되었을때(5번 로그인 실패시)
enabled값을 0으로 바꿔줌과 동시에 계정을 잠궈주는 로직을 빌드해야 한다.
failcnt를 불러오는 getFailCount()와
계정을 잠그는 lockAccount()메소드를 생성한다.
//LoginFailCountUp.java
private void loginFailCountUp(String email) {
msv.upFailCount(email);
int failCount = msv.getFailCount(email);
if(failCount >= 5) {
msv.lockAccount(email);
}
}
생성후에 마찬가지로 빨간줄이 뙇 갈텐데
그럴땐 뭐다????
Service --> DAO순으로 생성해준다.
//MemberService.java
public int getFailCount(String email);
public void lockAccount(String email);
//MemberServiceImpl.java
@Override
public int getFailCount(String email) {
return mdao.selectFailCount(email);
}
@Override
public void lockAccount(String email) {
mdao.updateAccountStatus(email);
}
//MemberDAO.java
public int selectFailCount(String email);
public void updateAccountStatus(String email);
//MemberDAOImpl.java
@Override
public int selectFailCount(String email) {
return sql.selectOne(ns+"getFailCount", email);
}
@Override
public void updateAccountStatus(String email) {
sql.update(ns+"lock", email);
}
작성후에는 Mapper단에서 sql문을 작성한다.
<select id="getFailCount" parameterType="java.lang.String" resultType="java.lang.Integer">
select failcnt <include refid="base"/>
</select>
<update id="lock" parameterType="java.lang.String">
update tbl_member set enabled = 0 where email=#{email}
</update>
이제 5번의 로그인 실패시 실패 카운트가 1씩 증가하고
failcnt가 결국 5이 되면 enabled가 0으로 바뀌고 계정이 잠긴다.
11. 로그인 성공했을때 초기화해주기(failcnt초기화, session초기화)
LoginSuccessHandler의 이름으로 실패시 핸들링 해주는 핸들러를 만든다고 선언한다.
//security-context.xml
authentication-success-handler-ref="loginSuccessHandler"
//아래쪽
<bean id="loginSuccessHandler" class="com.myweb.handler.LoginSuccessHandler">
<property name="authEmail" value="email"></property>
<property name="successUrl" value="/"></property>
</bean>
실제 src/main/java/com/myweb에 handler 패키지를 만들어 LoginSuccessHandler의 이름으로 클래스를 생성해준다.


- failcnt초기화
public class LoginSuccessHandler implements AuthenticationSuccessHandler{
private static Logger log = LoggerFactory.getLogger(LoginSuccessHandler.class);
private String authEmail;
private String successUrl;
@Inject
MemberService msv;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
//성공하면 failcnt초기화하기
msv.resetFailCount(email);
}
//여기서부터는 getter/setter만들어주기
}
이러면 Service에서 나 저 메소드 없다고 빨간줄이 뙇!!
Service부분에 resetFailCount()를 생성한다.
//MemberService.java
public void resetFailCount(String email);
//MemberServiceImpl.java
@Override
public void resetFailCount(String email) {
mdao.updateFailCountReset(email);
}
또 빨간줄이 뙇!
이번엔 DAO에 updateFailCountReset() 생성한다.
//MemberDAO.java
public void updateFailCountReset(String email);
//MemberDAOImpl.java
@Override
public void updateFailCountReset(String email) {
sql.update(ns+"reset", email);
}
이제 Mapper로 가서 update sql문을 작성한다.
<update id="reset" parameterType="java.lang.String">
update tbl_member set failcnt = 0 where email = #{email}
</update>
- session 초기화
먼저 저장된 session을 가져오는 RequestCache와
가져온 session을 보내주는 역할을 해주는 RedirectStrategy를 선언한다.
//LoginSucceessHandler.java
private RequestCache reqCache = new HttpSessionRequestCache(); // 가져오는 sessoin
private RedirectStrategy rdStg = new DefaultRedirectStrategy(); //어디로 보낼것인가
그리고 기존의 onAuthenticationSuccess메소드 안에 기존의 가지고 온 session이 존재할 경우
삭제하라는 로직을 작성한다.
HttpSession session = request.getSession(false);
if(session == null) {
return;
}else {
session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
}
response.sendRedirect(successUrl);
여기서, getSession은 Boolean타입으로 ()안에 아무것도 적지 않으면 새로운 session을 가져오게 되고
()안에 false를 적게되면 기존의 생성되었던 session을 가져오게 된다.

우리는 기존의 Session을 가져와서 삭제해야 하므로 false를 적어준다.
- 로그인 성공시 로그인회원정보를가지고 메인페이지로 가게 셋팅하기.(이건 처음에 셋팅한것!)
//security-context.xml
<bean id="userService" class="com.myweb.service.AuthMemberDetailService"></bean>
//AuthMemberDetailService.java
public class AuthMemberDetailService implements UserDetailsService{
private static Logger log = LoggerFactory.getLogger(AuthMemberDetailService.class);
@Inject
private MemberDAO mdao;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
MemberVO mvo = mdao.selectMember(email);
if(mvo == null) {
throw new UsernameNotFoundException(email);
}
return mvo;
}
}
12. 보던 페이지에서 로그인시 메인페이지가 아닌 보던 페이지로의 이동.
상품페이지에서 로그인시 메인화면으로 이동이 아닌 상품페이지로 이동해야한다.
처음부터 로그인을 누르고 로그인해서 들어오면
즉, 처음 메인페이지에서 로그인 해서 들어오면 우리가 다음에 어디로 갈 것인지에 대한 정보를 session으로부터 가져 올 수 없는데
상품리스트에서 로그인을 누르면 ctrl로 가지전에 security가 인터셉트해서 해당 url값을 저장하고 있기때문에
RequestCache 타입으로 http session에 저장되어 있는 전 url을 가져와서
로그인 후 RedirectStrategy를 통해 저장된 url로 보내준다.
따라서 상품리스트에서 로그인시 다시 상품리스트로 돌아갈 수 있게 해준다.!!!
RequestCache와RedirectStrategy 모두 Security가 가지고 있는 애들이다.
//LoginSuccessHandler.java
private RequestCache reqCache = new HttpSessionRequestCache(); // 가져오는 sessoin
private RedirectStrategy rdStg = new DefaultRedirectStrategy(); //어디로 보낼것인가
//onAuthenticationSuccess메소드안에
SavedRequest saveReq = reqCache.getRequest(request, response);
if(saveReq != null) {
String destPage = saveReq.getRedirectUrl();
rdStg.sendRedirect(request, response, destPage);
}else {
rdStg.sendRedirect(request, response, successUrl);
}
13. 접근제한에러 (403)을 다르게 처리하도록 설정
//security-context.xml
<security:access-denied-handler error-page="/access_denied"/>
//HomeController.java
//get방식으로오면 처리해서 그래도 value의 주소로 다시 나간다!!!!
@GetMapping("/access_denied")
public void denied(Locale locale, Model model) {
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate );
}
//access_denied.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<jsp:include page="common/header.jsp"></jsp:include>
<jsp:include page="common/nav.jsp"></jsp:include>
<div class="jumbotron">
<h1>접근 권한이 없는 페이지 입니다.</h1>
<p>관리자에게 문의하세요!</p>
<p>EMAIL : admin@admin.com</p>
<p>TEL : 010-0000-0000</p>
<p>현재시간은 ${serverTime }입니다.</p>
</div>
<jsp:include page="common/footer.jsp"></jsp:include>
14. spring security가 get을 제외하고 모든 곳에 적용시키기
- post방식들 수정.(get방식제외)
//wirte.jsp에 복붙
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
<form action="/product/write?${_csrf.parameterName}=${_csrf.token}" method="post" enctype="multipart/form-data">
<sec:authorize access="isAuthenticated()">
<div class="form-group">
<label for="title">등록자:</label> <input type="text"
class="form-control" name="writer" value=' <sec:authentication property="principal.email" />' readonly>
</div>
</sec:authorize>
//detail.jsp
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
<tr>
<th colspan="2">
<a href="/product/list?pageNum=${cri.pageNum }&amount=${cri.amount}&type=${cri.type}&keyword=${cri.keyword}"
class="btn btn-success">목록</a>
<sec:authorize access="isAuthenticated()">
<a href="/product/modify?pno=${pvo.pno }&pSign=0&pageNum=${cri.pageNum }&amount=${cri.amount}&type=${cri.type}&keyword=${cri.keyword}"
class="btn btn-warning">수정</a>
<a href="#" class="btn btn-outline-danger" id="delBtn">삭제</a>
</sec:authorize>
</th>
</tr>
//modify.jsp
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
//post방식이므로
<form action="/product/modify?${_csrf.parameterName}=${_csrf.token}" method="post" enctype="multipart/form-data">
//value수정
<div class="form-group">
<label for="title">등록자:</label> <input type="text"
class="form-control" name="writer" value="${pvo.writer }" readonly>
</div>
//삭제
//detail.jsp
<form action="/product/remove" id="delForm" method="post">
<input type="hidden" name="${_csrf.parameterName }" value="${_csrf.token }">
<input type="hidden" name="pno" value="${pvo.pno }"> <input
type="hidden" name="imgfile" value="${pvo.imgfile}"> <input
type="hidden" name="pageNum" value="${cri.pageNum }"> <input
type="hidden" name="amount" value="${cri.amount }"> <input
type="hidden" name="type" value="${cri.type }"> <input
type="hidden" name="keyword" value="${cri.keyword }">
</form>
//detail.jsp
<sec:authorize access="isAuthenticated()">
<div class="input-group mt-3">
<input type="text" id="cmtInput" class="form-control"
placeholder="댓글은 여기에..">
<div class="input-group-append">
<button class="btn btn-primary" type="button" id="cmtOkBtn">OK</button>
<button class="btn btn-danger" type="button" id="cmtResetBtn">Cancel</button>
</div>
</div>
</sec:authorize>
- ajax도 수정
방법1. 보낼때 csrf를 셋팅해서 보내는 방법
먼저 header.jsp에 우리가 받으려고 하는 csrf를 보낸다고 셋팅한다.
mete태그를 작성할 때는 viewport가 있는 meta태그 아랫쪽으로 작성한다.
//header.jsp
<meta name="_csrf_header" content="${_csrf.headerName }">
<mete name="_csrf" content="${_csrf.token }">
header.jsp에서 보낸것을 여기서 받아서 또 다른 곳으로 보낸다고 해준다.
detail.jsp의 클릭이벤트로 ajax통해서 데이터를 전송할 때 beforeSend를 사용하여 ajax이 전송되기 전에 header와 token을 xhr(xml http request)이라는 파라미터에 담아 보내란 의미로한다.
//detail.jsp
$("#cmtOkBtn").on("click",function(){
let cmt_content = $("#cmtInput").val();
if(cmt_content == null || cmt_content == ''){
alert("댓글 내용을 입력해주세요!");
return false;
}else{
let cmtData = {pno:cmt_pno,writer:cmt_writer,content:cmt_content};
let header = $("meta[name='_csrf_header']").attr("content");
let token = $("meta[name='_csrf']").attr("content");
$.ajax({
type: "post",
url:"/comment/new",
data: JSON.stringify(cmtData),
contentType:"application/json; charset:utf-8",
beforeSend : function(xhr){
xhr.setRequestHeader(header,token);
}
}).done(function(result){
alert(result);
listComment(cmt_pno);
});
}
});
그리고 여기선 댓글을 작성한 작성자가
email이 아니라 nickname이기 때문에 let cmt_writer = '<c:out value="${sesInfo.nickname}"/>'; 이 부분을 수정해줘야 한다.
.
.
.
.
.
-------------------------------------------------------end-------------------------------------------------------
'_Programming > Spring' 카테고리의 다른 글
Spring.IoC&DI (0) | 2020.07.21 |
---|---|
Spring.TeamProject (Cpos) (0) | 2020.07.13 |
Spring.AOP(Aspect-Oriented Programming) (0) | 2020.07.06 |
Spring.Comment (0) | 2020.07.02 |
Spring.Search(검색기능) (0) | 2020.07.02 |