1. 암호화 방법
· 입력된 패스워드는 인코딩을 통해 암호화되어 DB에 저장되고, 입력된 패스워드와 일치하는지 확인하기 위해 DB에서
조회한 패스워드는 디코딩을 거쳐 원래의 패스워드로 변경하여 확인하는 과정을 거친다.
· 회원가입 할 때, customNoOpPasswordEncoder의 모듈을 이용하여 password를 매칭시키고, bcryptPasswordEncoder
모듈을 통해 인코딩 과정을 거쳤으므로, password-encoder ref를 bcryptPasswordEncoder를 사용해야 디코딩이 가능하다.
<security-db-context.xml>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<beans:bean id="customNoOpPasswordEncoder" class="edu.bit.ex.security.CustomNoOpPasswordEncoder"/>
<!-- 암호화 모듈 -->
<beans:bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
<!-- provider -->
<authentication-manager>
<authentication-provider>
<password-encoder ref="bcryptPasswordEncoder"/>
<jdbc-user-service
data-source-ref="dataSource"
role-prefix=""
users-by-username-query="select username, password, enabled from users where username = ?"
authorities-by-username-query="select username, authority from authorities where username = ?"
/>
</authentication-provider>
</authentication-manager>
|
cs |
2. 스프링 시큐리티에서 커스텀마이징 방법
· xml에서는 username, password, enabled 세 가지 정보만 가져올 수 있기 때문에 커스텀마이징을 통해 확장하여
JSP에서 session 객체를 통해 다른 정보를 가져온다. ·
· MemberDetailsService 객체를 생성하여 로그인 하는 경우, Service의 loadUserByUsername을 호출한다.
<security-custom-context.xml>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/login/loginForm" access="permitAll" />
<intercept-url pattern="/" access="permitAll" />
<intercept-url pattern="/admin/**" access="hasRole('ADMIN')" />
<intercept-url pattern="/**" access="hasAnyRole('USER', 'ADMIN')" />
<!--로그인 페이지 커스텀 화 -->
<form-login login-page="/login/loginForm"
default-target-url="/"
authentication-failure-url="/login/loginForm?error"
username-parameter="id"
password-parameter="password" />
<logout logout-url="/logout" logout-success-url="/" />
<!-- 403 에러 처리 -->
<access-denied-handler error-page="/login/accessDenied"/>
</http>
<beans:bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
<beans:bean id="customNoOpPasswordEncoder" class="edu.bit.ex.security.CustomNoOpPasswordEncoder"/>
<beans:bean id="memberDetailsService" class="edu.bit.ex.security.MemberDetailsService" />
<authentication-manager>
<authentication-provider user-service-ref="memberDetailsService">
<password-encoder ref="customNoOpPasswordEncoder"/>
</authentication-provider>
</authentication-manager>
</beans:beans>
|
cs |
<HomeController.java>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
@Log4j
@Controller
public class HomeController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
log.info("Welcome home! The client locale is {}.");
return "home";
}
@GetMapping("/user/userHome")
public void userHome() {
log.info("userHome ...");
}
@GetMapping("/admin/adminHome")
public void adminHome() {
log.info("adminHome ...");
}
@GetMapping("/login/accessDenied")
public void accessDenied(Model model) {
log.info("Welcome Access Denied!");
}
@GetMapping("/login/loginForm")
public String loginForm() {
log.info("Welcome Login Form!");
return "login/loginForm2";
}
}
|
cs |
<home.jsp>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<body>
<h1>메인페이지</h1>
<sec:authorize access="isAnonymous()">
<p><a href="<c:url value="/login/loginForm" />">로그인</a></p>
</sec:authorize>
<sec:authorize access="isAuthenticated()">
<form:form action="${pageContext.request.contextPath}/logout" method="POST">
<input type="submit" value="로그아웃" />
</form:form>
<p><a href="<c:url value="/loginInfo" />">로그인 정보 확인 방법3 가지</a></p>
</sec:authorize>
<h3>
[<a href="<c:url value="/user/userForm" />">회원가입</a>]
[<a href="<c:url value="/user/userHome" />">유저 홈</a>]
[<a href="<c:url value="/admin/adminHome" />">관리자 홈</a>]
</h3>
</body>
|
cs |
<AuthVO.java>
1
2
3
4
5
6
7
8
9
|
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@ToString
public class AuthVO {
private String username;
private String authority;
}
|
cs |
<MemberVO.java>
1
2
3
4
5
6
7
8
9
10
11
12
|
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@ToString
public class MemberVO {
private String username;
private String password;
private String enabled;
private List<AuthVO> authList;
}
|
cs |
<MemberMapper.java>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<mapper namespace="edu.bit.ex.mapper.MemberMapper">
<resultMap type="edu.bit.ex.vo.MemberVO" id="memberMap">
<result column="username" property="username" />
<result column="password" property="password" />
<result column="enabled" property="enabled" />
<collection property="authList" resultMap="authMap" />
</resultMap>
<resultMap type="edu.bit.ex.vo.AuthVO" id="authMap">
<result column="username" property="username" />
<result column="authority" property="authority" />
</resultMap>
<select id="getMember" resultMap="memberMap">
<![CDATA[
select * from users, authorities where users.username=authorities.username and users.username = #{username}
]]>
</select>
</mapper>
|
cs |
<MemberDetailsService.java> UserDetails 타입으로 리턴 하기 위해 그에 맞는 타입으로 변환!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@Log4j
@Service
public class MemberDetailsService implements UserDetailsService {
@Setter(onMethod_ = @Autowired)
private MemberMapper memberMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.warn("Load User By MemberVO number: " + username);
MemberVO vo = memberMapper.getMember(username);
log.warn("queried by MemberVO mapper: " + vo);
return vo == null ? null : new MemberUser(vo);
}
}
|
cs |
<MemberUser.java>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
@Setter
@Getter
@ToString
public class MemberUser extends User {
private MemberVO member;
// 기본적으로 부모의 생성자를 호출해야만 정상적으로 작동
public MemberUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
}
public MemberUser(MemberVO memberVO) {
super(memberVO.getUsername(), memberVO.getPassword(), getAuth(memberVO));
this.member = memberVO;
}
// 유저가 갖고 있는 권한 목록
public static Collection<? extends GrantedAuthority> getAuth(MemberVO memberVO) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (AuthVO auth : memberVO.getAuthList()) {
authorities.add(new SimpleGrantedAuthority(auth.getAuthority()));
}
return authorities;
}
}
|
cs |
<userHome.jsp>
1
2
3
4
5
6
7
8
9
10
11
12
|
<html lang="ko">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>유저 페이지</title>
</head>
<body>
<h1>유저 페이지 입니다.</h1>
<p>principal: <sec:authentication property="principal"/></p>
<p>사용자이름: <sec:authentication property="principal.member.username"/></p>
<p><a href="<c:url value="/" />">홈</a></p>
</body>
</html>
|
cs |
<로그인 전/후>
<유저 홈, member 정보 출력 화면>