오늘은 저번에 DB 사용하지 않고 구현 해봤는데, 이번에는 jdbc 를 이용해 사용해 보도록 하겠습니다.
처음에는 pom.xml에 몇가지 라이브러리를 추가하겠습니다.
을 추가하고
룸복은 라이브러리를 받기만 해서 되는게 아니라
https://projectlombok.org/download
Download
projectlombok.org
룸복 정식 홈페이지에서 다운을 받아 실행시켜 줘야 합니다.
java -jar lombook.jar
[Specify location] 버튼을 눌러 Eclipse에 있는 eclipse.ini를 open 시켜줍니다.
[Quit installer] 를 눌러 룸복을 설치합니다.
저번에 만든 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-4.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="AccessDenied" class="com.spring.example.security.LoginSuccessHandler"></bean>
<bean id="DeniedHandler" class="com.spring.example.security.DeniedHandler"></bean>
<bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
<bean id="customUserDetails" class="com.spring.example.security.CustomUserDetailsService" />
<security:http>
<security:intercept-url pattern="/security/all" access="permitAll" />
<security:intercept-url pattern="/security/member" access="hasRole('ROLE_MEMBER')" />
<security:access-denied-handler ref="DeniedHandler" />
<security:form-login login-page="/login"
authentication-success-handler-ref="AccessDenied" />
</security:http>
<security:authentication-manager>
<security:authentication-provider user-service-ref="customUserDetails">
<security:password-encoder ref="bcryptPasswordEncoder" />
</security:authentication-provider>
</security:authentication-manager>
</beans>
소스는 사진에 있는거랑 같은 소스입니다.
이 다음 security-context.xml 위 쪽에 <bean id = ""> 로 되어있는 걸 만들어 주도록 하겠습니다.
CustomUser.java 는
package com.spring.example.security;
import java.util.Collection;
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 com.spring.example.VO.MemberVO;
import lombok.Getter;
@Getter
public class CustomUser extends User{
private static final long serialVersionUID = 1L;
private MemberVO memberVO;
public CustomUser(String id, String password,
Collection<? extends GrantedAuthority> auth) {
super(id, password, auth);
}
public CustomUser(MemberVO vo) {
super(vo.getId(), vo.getPassword(), vo.getAuthList().stream()
.map(auth -> new SimpleGrantedAuthority(auth.getAuth()))
.collect(Collectors.toList()));
this.memberVO = vo;
}
}
이렇게 써주시고요.
CustomUserDetailsService.java는
package com.spring.example.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 com.spring.example.VO.MemberVO;
import com.spring.example.dao.MemberDao;
import lombok.Setter;
public class CustomUserDetailsService implements UserDetailsService{
@Setter(onMethod_ = { @Autowired })
private MemberDao memberDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
MemberVO vo = new MemberVO();
try {
vo = memberDao.Login(username);
} catch(Exception e) {
e.printStackTrace();
}
return vo == null ? null : new CustomUser(vo);
}
}
DeninedHandler.class 예외 발생이 생겼을떄의 코드인데요 이거는
package com.spring.example.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.*;
public class DeniedHandler implements AccessDeniedHandler{
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
System.out.println("에러 났습니다.");
response.sendRedirect("/security/accessError");
}
}
LoginSuccessHandler.java는 로그인이 성공 되었을 떄 권한에 따라 화면이 움직는 역활을 하는데 이 소스는 밑에와 같습니다.
package com.spring.example.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.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
public class LoginSuccessHandler implements AuthenticationSuccessHandler{
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
List<String> rolenames = new ArrayList<String>();
System.out.println("로그인 성공");
authentication.getAuthorities().forEach(authority -> {
rolenames.add(authority.getAuthority());
});
if(rolenames.contains("ROLE_ADMIN")) {
response.sendRedirect("/security/admin");
return;
}
if(rolenames.contains("ROLE_MEMBER")) {
response.sendRedirect("/security/member");
return;
}
response.sendRedirect("/");
}
}
여기서 저희가 만들어둔 MemberVO 와 AuthVO를 수정하고 만들어야 제대로 진행이 될것같습니다.
AuthVO.java 는
package com.spring.example.VO;
public class AuthVO {
private String userid;
private String auth;
public String getUserid() {
return userid;
}
public void setUserid(String userid) {
this.userid = userid;
}
public String getAuth() {
return auth;
}
public void setAuth(String auth) {
this.auth = auth;
}
}
로 만들어주시고요.
MemberVO는 이렇게 수정해주세요.
package com.spring.example.VO;
import java.util.Date;
import java.util.List;
public class MemberVO {
private String id;
private String password;
private String userName;
private boolean enabled;
private Date regdate;
private List<AuthVO> authList;
// getter, setter
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Date getRegdate() {
return regdate;
}
public void setRegdate(Date regdate) {
this.regdate = regdate;
}
public List<AuthVO> getAuthList() {
return authList;
}
public void setAuthList(List<AuthVO> authList) {
this.authList = authList;
}
// 생성자
public MemberVO() {
}
// to String
@Override
public String toString() {
return "MemberVO [id=" + id + ", password=" + password + "]";
}
}
위 쪽에 에러가 났을때 가는 컨트롤러를 지정해주겠습니다.
package com.spring.example.Controller;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping("/security/*")
@Controller
public class SecurityController {
@GetMapping("/all")
public String all() {
return "home";
}
@GetMapping("/member")
public String member() {
return "security/member";
}
@GetMapping("/admin")
public String admin() {
return "security/admin";
}
@GetMapping("/accessError")
public String error(AuthenticationSuccessHandler auth, Model model) {
model.addAttribute("msg", "Access Demoed");
return "common/LoginPage";
}
}
로 지정해주시고요.
에러가 날 시 LoginPage로 이동하게 해주세요.
그리고 저번 시간에 만든 로그인 체크 해주는 Service, ServiceImple, dao 를 수정해줘야 합니다. 기존에는 vo를 넣어 id와 Password를 체크해줬지만 시큐리티에서는 id 를 체크하고 password를 쿼리를 가져오는 역활을 해줘 쿼리에 id 만 필요하기 때문에 수정하도록 하겠습니다.
package com.spring.example.dao;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.spring.example.VO.MemberVO;
@Repository
public class MemberDao {
@Autowired
private SqlSessionTemplate mybatis;
public MemberVO Login(String userid) throws Exception {
return mybatis.selectOne("Member.Login", userid);
}
}
package com.spring.example.service;
import com.spring.example.VO.MemberVO;
public interface MemberService {
public MemberVO Login(String userid) throws Exception;
}
package com.spring.example.serviceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.spring.example.VO.MemberVO;
import com.spring.example.dao.MemberDao;
import com.spring.example.service.MemberService;
@Service
public class MemberServiceImpl implements MemberService{
@Autowired
private MemberDao memberDao;
@Override
public MemberVO Login(String userid) throws Exception {
return memberDao.Login(userid);
}
}
이렇게 매개변수는 String으로 변경하고 return 타입은 vo 가 되도록 변경해줍니다.
쿼리문도 바꿔야 하기 때문에 이렇게 변경해주시고요.
<?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="Member">
<resultMap type="MemberVO" id="memberMap">
<id property="id" column="userid" />
<result property="id" column="userid" />
<result property="password" column="userpw" />
<result property="userName" column="username" />
<result property="regdate" column="regdate" />
<collection property="authList" resultMap="authMap">
</collection>
</resultMap>
<resultMap type="AuthVO" id="authMap">
<result property="userid" column="userid" />
<result property="auth" column="auth" />
</resultMap>
<select id="Login" parameterType="String" resultMap="memberMap">
SELECT a.userid userid
, a.userpw userpw
, a.username username
, a.regdate regdate
, a.enabled
, b.auth as auth
from users a
LEFT JOIN authorities b
ON a.userid= b.userid
where a.userid = #{userid}
</select>
</mapper>
AuthVO도 mybatis 가 잘 찾을 수 있도록 설정을 해줘야합니다.
mybatis-config.xml 를 변경해주세요.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0/EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias alias="MemberVO" type="com.spring.example.VO.MemberVO" />
<typeAlias alias="AuthVO" type="com.spring.example.VO.AuthVO" />
</typeAliases>
</configuration>
변경된 쿼리 처럼 Table를 추가해주어야 합니다.
create table users(
userid varchar(50) not null primary key,
userpw varchar(100) not null,
username varchar(100) not null,
regdate TIMESTAMP default CURRENT_TIMESTAMP,
enabled char(1) default '1'
);
create table authorities (
userid varchar(50) not null,
auth varchar(50) not null
);
insert into authorities values ( 'member', 'ROLE_MEMBER');
그리고 jUnit을 통해 users 테이블에 인코딩된 패스워드를 넣어 보도록 하겠습니다.
( 꼭 test 폴더에 넣어서 실행해야 합니다. )
package com.spring.example.security;
import java.sql.Connection;
import java.sql.PreparedStatement;
import javax.sql.DataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import lombok.Setter;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({
"file:src/main/webapp/WEB-INF/spring/root-context.xml",
"file:src/main/webapp/WEB-INF/spring/security-context.xml"
})
public class MemberTest {
@Setter(onMethod_ = @Autowired)
private PasswordEncoder pwencoder;
@Setter(onMethod_ = @Autowired)
private DataSource ds;
@Test
public void testInserMember() {
String sql = "insert into users(userid, userpw, username) values(?, ?, ?)";
Connection con = null;
PreparedStatement pstmt = null;
try {
con = ds.getConnection();
pstmt = con.prepareStatement(sql);
pstmt.setString(1, "member");
pstmt.setString(2, pwencoder.encode("member"));
pstmt.setString(3, "member");
pstmt.executeUpdate();
} catch(Exception e) {
e.printStackTrace();
System.out.println("테스트");
}
}
}
를 실행 하면 쿼리문에 member가 들어가게 됩니다.
실행 방법은
를 클릭하면
JUnit으로 실행 해 줍니다.
쿼리를 실행해보면
uses 테이블에 이상하게 데이터가 들어가면 성공하게 된것입니다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>로그인</title>
<style>
form{
text-align: center;
margin:0 auto;
}
.border{
margin:0 auto;
width:400px;
height:500px;
border:1px solid #000;
border-radius: 10%;
}
img{
margin-top:20px;
margin-bottom:80px;
}
input{
width:300px;
}
</style>
</head>
<body>
<form action ="/login" method="POST">
<div class="border">
<h1>로그인</h1>
<img src ="/images/profle.jpg"><br>
<c:if test="${empty msg ? true : false}" var="result">
<h2><c:out value="${msg}" /></h2>
</c:if>
<input type="text" name="username" id="username" placeholder="아이디를 입력해주세요."><br>
<input type="password" name="password" id="password" placeholder="비밀번호를 입력해주세요."><br><br>
<input type="submit" value="로그인">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</div>
</form>
</body>
</html>
로그인도 csrf 속성 때문에
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
를 추가하였습니다.
자, 이제 서버를 다시 실행 시켜 로그인 페이지로 가서
아이디는 member 비밀번호도 member로 입력하게 되면
ROLE_MEMBER 권한 은 security/member로 가게 LoginSuccessHendler.java 에 설정했기 때문에 member 페이지로 가게 되었습니다.
'프로그래밍 > 스프링(spring)' 카테고리의 다른 글
[Spring] 스프링 IoC, Bean, DI 정리 (0) | 2022.06.15 |
---|---|
[스프링/spring] 스프링 시큐리티 (spring security) - DB 사용안함 (0) | 2020.04.12 |
[스프링/spring] 스프링 로그인 기능 만들기(DB 사용) (10) | 2020.04.12 |
[스프링/java] 스프링 메인화면 만들어 보기 (2) | 2020.04.12 |
[스프링/spring] 스프링 데이터베이스(DB) 연결 (0) | 2020.04.12 |