security DB 설정
https://lavender1122.tistory.com/206
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:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="customAccessDenied"
class="kr.or.ddit.security.CustomAccessDeniedHandler">
</bean>
<bean id="customLoginSuccess"
class="kr.or.ddit.security.CustomLoginSuccessHandler">
</bean>
<bean id="customUserDetailsService2"
class="kr.or.ddit.security.CustomUserDetailsService2">
</bean>
<!-- 비밀번호 암호화 처리글 -->
<bean id="passwordEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
</bean>
<security:http>
<!-- 사용자정의 로그인 성공 처리자를 지정함 -->
<security:form-login login-page="/login"
authentication-success-handler-ref="customLoginSuccess"/>
<security:access-denied-handler ref="customAccessDenied"/>
<security:remember-me data-source-ref="dataSource"
token-validity-seconds="604822"
/>
<security:logout logout-url="/logout"
invalidate-session="true"
delete-cookies="remember-me,JSESSION_ID"
/>
</security:http>
<security:authentication-manager>
<!--지정된 아이디와 패스워드로 로그인이 가능하도록 설정함 -->
<security:authentication-provider user-service-ref="customUserDetailsService2">
<!-- 비밀번호 암호화 -->
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
</beans>
CustomAccessDeniedHandler.java 생성
package kr.or.ddit.security;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class CustomAccessDeniedHandler implements AccessDeniedHandler{@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
org.springframework.security.access.AccessDeniedException accessDeniedException)
throws IOException, ServletException {
log.info("handle");
// response.sendRedirect("/accessError");
response.sendRedirect("loginForm");
}
/*
공지사항 등록 화면(/notice/register)은
일반회원(member/1234)이 접근할 수 없는 페이지이고,
관리자(admin/1234)만 접근 가능하므로..
지정된 접근 거부 처리자(CustomAccessDeniedHander)에서
접근 거부 처리 페이지(/accessError)로 리다이렉트 시킴
*/
}
CustomLoginSuccessHandler.java
package kr.or.ddit.security;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.jaas.AuthorityGranter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import lombok.extern.slf4j.Slf4j;
/* /notice/register -> loginForm -> 로그인 -> CustomLoginSuccessHandler(성공)
-> 사용자 작업.. -> /notice/register 로 리다이렉트 해줌
(스프링 시큐리티에서 기본적으로 사용되는 구현 클래스)
*/
@Slf4j
public class CustomLoginSuccessHandler extends
SavedRequestAwareAuthenticationSuccessHandler{
//부모 클래스의 메소드를 재정의
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication auth) throws ServletException, IOException {
log.info("onAuthenticationSuccess");
//auth.getPrincipal() : 사용자 정보를 가져옴
//시큐리티에서 사용자 정보는 User 클래스의 객체로 저장됨(CustomUser.java를 참고)
User costomUser = (User)auth.getPrincipal();
//사용자 아이디를 리턴
log.info("username : " + costomUser.getUsername());
//auth.getAuthorities() -> 권한들(ROLE_MEMBER,ROLE_ADMIN)
//authority.getAuthority() : ROLE_MEMBER
List<String> roleNames = new ArrayList<String>();
auth.getAuthorities().forEach(authority->{
roleNames.add(authority.getAuthority());
});
log.info("roleNames : " + roleNames);
//부모에게 줌
//가던길 쭉
super.onAuthenticationSuccess(request, response, auth);
}
}
CustomUserDetailsService2.java 생성
Add 클릭 > UserDetailsService 인터페이스 지정
class 이기 때문에 @Service 어노테이션 사용한다
package kr.or.ddit.security;
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 kr.or.ddit.mapper.MemberMapper;
import kr.or.ddit.vo.MemberVO;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class CustomUserDetailsService2 implements UserDetailsService {
@Autowired
private MemberMapper memberMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("CustomUserDetailsService2 ->username "+username);
//select
MemberVO memberVO = this.memberMapper.detail(username);
log.info("CustomUserDetailsService2-> memberVO"+memberVO);
//User(스프링꺼)
//CustomUser2(우리꺼)
return memberVO==null?null:new CumstomUser2(memberVO);
}
}
select 생성
인터페이스 이기 때문에 어노테이션을 안사용하는 대신
root-context.xml bean 객체 생성 해야한다
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="kr.or.ddit.**.mapper" />
</bean>
SQL.xml 생성
package kr.or.ddit.mapper;
import kr.or.ddit.vo.MemberVO;
public interface MemberMapper {
public MemberVO detail(String username);
}
<?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="kr.or.ddit.mapper.MemberMapper">
<resultMap type="memberVO" id="memberMap">
<result property="userId" column="USER_ID"/>
<result property="userName" column="USER_NAME"/>
<result property="password" column="PASSWORD"/>
<result property="regDate" column="REG_DATE"/>
<result property="coin" column="COIN"/>
<result property="enabled" column="ENABLED"/>
<collection property="memberAuthVOList" resultMap="memberAuthMap"></collection>
</resultMap>
<resultMap type="memberAuthVO" id="memberAuthMap">
<result property="userId" column="USER_ID"/>
<result property="auth" column="AUTH"/>
</resultMap>
<!-- 부모(MEMBER)1 : 자식(MEMBER_AUTH) N -->
<select id="detail" parameterType="String" resultMap="memberMap" >
SELECT A.USER_ID, A.USER_NAME, A.PASSWORD, A.REG_DATE, A.COIN, A.ENABLED
, B.AUTH
FROM MEMBER A, MEMBER_AUTH B
WHERE A.USER_ID =B.USER_ID
AND A.USER_ID=#{username}
</select>
</mapper>
VO설정
프로퍼티 : userName, password, enabled 있는지 확인
중첩된 자바빈 생성
package kr.or.ddit.vo;
import lombok.Data;
@Data
public class MemberAuthVO {
private String userId;
private String auth;
}
mybatisAlias.xml 설정
MemberAuthVO 추가하기
CumstomUser 생성
기본생성자 생성
package kr.or.ddit.security;
import java.util.Collection;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import kr.or.ddit.vo.MemberVO;
public class CumstomUser2 extends User {
public CumstomUser2(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
// TODO Auto-generated constructor stub
}
public CumstomUser2(MemberVO memberVO) {
super(memberVO.getUserId(),memberVO.getPassword(),
memberVO.getMemberAuthVOList().stream()
.map(auth->new SimpleGrantedAuthority(auth.getAuth()))
.collect(Collectors.toList())
);
}
}
Collection<? extends GrantedAuthority> authorities 데이터 넣기 위해서
memberVO.getMemberAuthVOList().stream() .map(auth->new SimpleGrantedAuthority(auth.getAuth())) .collect(Collectors.toList())
넣는다
user객체(부모객체)에 username, password,authorities 넣어줌
사용자가 유저를 정의함
memberVO(select결과)정보를 User(스프링 시큐리티에서 정의된 유저) 객체 정보에 연계하여 넣어줌
CustomUser의 객체 = principal => jsp 사용
스프링 시큐리티 표현식
로그인 안 한 경우
<sec:authorize access="isAnonymous()">
<div class="user-panel mt-3 pb-3 mb-3 d-flex">
<div class="image">
<img src="/resources/adminlte/dist/img/user2-160x160.jpg" class="img-circle elevation-2" alt="User Image">
</div>
<div class="info">
<a href="#" class="d-block"></a>
</div>
</div>
</sec:authorize>
로그인 한 경우
<sec:authorize access="isAuthenticated()">
</sec:authorize>
principal 사용자객체처리
<sec:authentication property="principal.memberVO" var="member" />
<sec:authentication property="principal.memberVO.memberAuthVOList"
var="memberAuthVOList"/>
<c:forEach var="memberAuthVO" items="${memberAuthVOList}" varStatus="stat">
<p>${memberAuthVO.auth}</p>
</c:forEach>
<div class="user-panel mt-3 pb-3 mb-3 d-flex">
<div class="image">
<img src="/resources/2024/05/11/d744b7c7-b4a1-4fda-8585-d74cce2723a6_bara1.jpg" class="img-circle elevation-2" alt="User Image">
</div>
<div class="info">
<a href="#" class="d-block">${member.userName}</a>
<sec:authentication property="principal.memberVO.memberAuthVOList"/>
<sec:authentication property="principal.memberVO.memberAuthVOList"
var="memberAuthVOList"/>
<c:forEach var="memberAuthVO" items="${memberAuthVOList}" varStatus="stat">
<p>${memberAuthVO.auth}</p>
</c:forEach>
<sec:authorize access="isAuthenticated()">
<sec:authentication property="principal.memberVO" var="member" />
<sec:authentication property="principal.memberVO.memberAuthVOList"
var="memberAuthVOList"/>
<c:forEach var="memberAuthVO" items="${memberAuthVOList}" varStatus="stat">
<p>${memberAuthVO.auth}</p>
</c:forEach>
<div class="user-panel mt-3 pb-3 mb-3 d-flex">
<div class="image">
<img src="/resources/2024/05/11/d744b7c7-b4a1-4fda-8585-d74cce2723a6_bara1.jpg" class="img-circle elevation-2" alt="User Image">
</div>
<div class="info">
<a href="#" class="d-block">${member.userName}</a>
<form action="/logout" method="post">
<button type="submit" class="btn btn-block btn-secondary btn-xs">로그아웃</button>
<sec:csrfInput />
</form>
</div>
</div>
</sec:authorize>
자동 로그인 기능
세션의 아이디를 쿠키에 저장
다시 로그인 안해도 되는 이유 : 쿠키에 저장되어있어서
CREATE TABLE PERSISTENT_LOGINS(
USERNAME VARCHAR2(200),
SERIES VARCHAR2(200),
TOKEN VARCHAR2(200),
LAST_USED DATE,
CONSTRAINT PK_PL PRIMARY KEY(SERIES)
);
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:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="customAccessDenied"
class="kr.or.ddit.security.CustomAccessDeniedHandler">
</bean>
<bean id="customLoginSuccess"
class="kr.or.ddit.security.CustomLoginSuccessHandler">
</bean>
<bean id="customUserDetailsService2"
class="kr.or.ddit.security.CustomUserDetailsService2">
</bean>
<!-- 비밀번호 암호화 처리글 -->
<bean id="passwordEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
</bean>
<security:http>
<!-- 사용자정의 로그인 성공 처리자를 지정함 -->
<security:form-login login-page="/login"
authentication-success-handler-ref="customLoginSuccess"/>
<security:access-denied-handler ref="customAccessDenied"/>
<security:remember-me data-source-ref="dataSource"
token-validity-seconds="604822"
/>
<security:logout logout-url="/logout"
invalidate-session="true"
delete-cookies="remember-me,JSESSION_ID"
/>
</security:http>
<security:authentication-manager>
<!--지정된 아이디와 패스워드로 로그인이 가능하도록 설정함 -->
<security:authentication-provider user-service-ref="customUserDetailsService2">
<!-- 비밀번호 암호화 -->
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
</beans>
loginForm.jsp
로그인 상태 유지 체크박스. 체크 시 : PERSISTENT_LOGINS 테이블에 로그인 로그 정보가 insert 됨
<input type="checkbox" name="remember-me" id="remember"> <label
for="remember"> 자동 로그인 </label>
name="remember-me" 중요!!
쿠키 확인
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
//request객체 안에서 있는 쿠키들을 확인
Cookie[] cookies=request.getCookies();
out.print("쿠키의 갯수"+cookies.length+"<br/>");
for(int i =0;i<cookies.length;i++){
out.print(cookies[i].getName()+":"+cookies[i].getValue()+"<br/>");
}
%>
<h3>register.jsp</h3>
<h2>로그인 한 회원만 접근 가능</h2>
<h3>/notice/register.jsp</h3>
어노테이션
환경설정
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans https://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/security
http://www.springframework.org/schema/security/spring-security-4.2.xsd">
<security:global-method-security pre-post-annotations="enabled"
secured-annotations="enabled"/>
</beans:beans>
스프링 시큐리티 애너테이션을 활성화
- pre-post-annotations="enabled" -> 골뱅이PreAuthorize, 골뱅이PostAuthorize 활성화 *** PreAuthorize : 특정 메소드를 실행하기 전에 role 체킹 PostAuthorize : 특정 메소드를 실행한 후에 role 체킹 - secured-annotations="enabled" -> 골뱅이Secured를 활성화 Secured : 스프링 시큐리티 모듈을 지원하기 위한 애너테이션
@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_MEMBER')")
랑
@PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_MEMBER')")
랑
@Secured({"ROLE_MEMBER","ROLE_ADMIN"})
같은 의미
@PreAuthorize("isAuthenticated()")
@Slf4j
@RequestMapping("/employee")
@Controller
public class EmployeeController {
EmployeeController 메소드안에 있는 메소드 접근할려면 로그인 해야된다
권한은 상관없음
1. 공지사항 등록 - 로그인 한 관리자만 접근 가능
@PreAuthorize("hasRole('ROLE_ADMIN')")
@PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_MEMBER')")
2. 공지사항 등록 - 로그인(인증) 한 관리자 또는 회원(인가)만 접근 가능
@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_MEMBER')")
@Secured({"ROLE_MEMBER","ROLE_ADMIN"})
3. 공지사항 등록 - 로그인(인증) 한 관리자 이면서 회원(인가)만 접근 가능
@PreAuthorize("hasRole('ROLE_ADMIN') and hasRole('ROLE_MEMBER')")
4. 로그인한 사용자만 접근 가능(권한과 상관 없음)
@PreAuthorize("isAuthenticated()")
5. 로그인 안 한 사용자가 접근 가능 -> 로그인 한 사용자는 접근 불가
@PreAuthorize("isAnonymous()")
'JAVA > spring' 카테고리의 다른 글
최종프로젝트 환경설정 (0) | 2024.06.06 |
---|---|
공통 코드 정보 + 중첩된 자바빈 (0) | 2024.05.25 |
SQL.xml 에서 join쿼리 사용하는 경우 (resultMap) (0) | 2024.05.15 |
페이징 처리 (0) | 2024.05.14 |
파일업로드 (0) | 2024.05.11 |