
๊ถํ ํ ์ด๋ธ
## ๊ถํ ํ
์ด๋ธ
CREATE TABLE authority
(
member_id INT NOT NULL REFERENCES member (id),
name VARCHAR(20) NOT NULL,
PRIMARY KEY (member_id, name)
);
MemberController.java
@GetMapping("list")
@PreAuthorize("hasAuthority('SCOPE_admin')")
public List<Member> list() {
return service.list();
}
@PreAuthorize("hasAuthority('SCOPE_admin')")์ ์ฌ์ฉํด์ admin ๊ถํ์ ๊ฐ์ง ์ฌ๋๋ง ํ์ ๋ชฉ๋ก์ ๋ณผ ์ ์๊ฒ ํฉ๋๋ค.MemberService.java
public Map<String, Object> getToken(Member member) {
Map<String, Object> result = null;
Member db = mapper.selectByEmail(member.getEmail());
if (db != null) {
if (passwordEncoder.matches(member.getPassword(), db.getPassword())) {
result = new HashMap<>();
String token = "";
List<String> authority = mapper.selectAuthorityByMemberId(db.getId());
String authorityString = authority.stream().collect(Collectors.joining(" "));
// ํ ํฐ ๋ง๋๋ ์ฝ๋
JwtClaimsSet claims = JwtClaimsSet.builder()
.issuer("self")
.issuedAt(Instant.now())
.expiresAt(Instant.now().plusSeconds(60 * 60 * 24 * 7)) //์ผ์ฃผ์ผ
.subject(db.getId().toString())
.claim("scope", authorityString) //๊ถํ
.claim("nickName", db.getNickName())
.build();
token = jwtEncoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
result.put("token", token);
}
}
return result;
}
mapper.selectAuthorityByMemberId(db.getId()) : ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ฃผ์ด์ง ๋ฉค๋ฒ ID์ ๋ํ ๊ถํ์ ๋ฆฌ์คํธ๋ก ๊ฐ์ ธ์ต๋๋ค.MemberMapper.java
@Select("SELECT name FROM authority WHERE member_id = #{memberId}")
List<String> selectAuthorityByMemberId(Integer memberId);
ํ์ ๋ชฉ๋ก admin ๊ถํ๋ง ์ ๊ทผ ๊ฐ๋ฅ
LoginProviedr.jsx
const [authority, setAuthority] = useState([]);
function isAdmin() {
return authority.includes("admin");
}
Navbar.jsx(React)
{account.isAdmin() && (
<Box
onClick={() => navigate("/member/list")}
cursor={"pointer"}
_hover={{ bgColor: "gray.200" }}
>
ํ์๋ชฉ๋ก
</Box>
)}
MemberService.java
public boolean hasAccess(Integer id, Authentication authentication) {
boolean self = authentication.getName().equals(id.toString());
boolean isAdmin = authentication.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("SCOPE_admin"));
return self || isAdmin;
}
MemberView.jsx
{account.hasAccess(member.id) && (
<Box mt={"30px"}>
<Button
ml={"10px"}
onClick={() => navigate(`/member/edit/${member.id}`)}
colorScheme={"green"}
>
์์
</Button>
<Button ml={"10px"} onClick={onOpen} colorScheme={"red"}>
ํํด
</Button>
</Box>
)}
MemberController.java
@PutMapping("modify")
@PreAuthorize("isAuthenticated()")
public ResponseEntity modify(@RequestBody Member member, Authentication authentication) {
if (service.hasAccessModify(member, authentication)) {
Map<String, Object> result = service.modify(member, authentication);
return ResponseEntity.ok(result);
} else {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
}
MemberService.java
public Map<String, Object> modify(Member member, Authentication authentication) {
~~~~
String token = "";
Jwt jwt = (Jwt) authentication.getPrincipal();
Map<String, Object> claims = jwt.getClaims();
JwtClaimsSet.Builder jwtClaimsSetBuilder = JwtClaimsSet.builder();
claims.forEach(jwtClaimsSetBuilder::claim);
jwtClaimsSetBuilder.claim("nickName", member.getNickName());
JwtClaimsSet jwtClaimsSet = jwtClaimsSetBuilder.build();
token = jwtEncoder.encode(JwtEncoderParameters.from(jwtClaimsSet)).getTokenValue();
return Map.of("token", token);
}
Jwt jwt = (Jwt) authentication.getPrincipal();: ํ์ฌ ์ธ์ฆ๋ ์ฌ์ฉ์์ ์ ๋ณด๊ฐ ๋ด๊ธด JWT๋ฅผ ๊ฐ์ ธ์ต๋๋ค.Map<String, Object> claims = jwt.getClaims();: JWT์์ ํด๋ ์(claim) ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ต๋๋ค. ํด๋ ์์ ํ ํฐ์ ์ ์ฅ๋ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์๋ฏธํฉ๋๋ค.JwtClaimsSet.Builder jwtClaimsSetBuilder = JwtClaimsSet.builder();: JWT ํด๋ ์ ์
์ ์์ฑํ๊ธฐ ์ํ ๋น๋๋ฅผ ์ด๊ธฐํํฉ๋๋ค.claims.forEach(jwtClaimsSetBuilder::claim);: ๊ฐ์ ธ์จ ํด๋ ์ ์ ๋ณด๋ฅผ ๋ฐ๋ณตํ์ฌ ํด๋ ์ ๋น๋์ ์ถ๊ฐํฉ๋๋ค.jwtClaimsSetBuilder.claim("nickName", member.getNickName());: ํ์ ์ ๋ณด ์ค ๋๋ค์์ ์๋ก์ด ํด๋ ์์ผ๋ก ์ถ๊ฐํฉ๋๋ค.JwtClaimsSet jwtClaimsSet = jwtClaimsSetBuilder.build();: ํด๋ ์ ๋น๋๋ก๋ถํฐ ์ต์ข
์ ์ธ JWT ํด๋ ์ ์
์ ์์ฑํฉ๋๋ค.token = jwtEncoder.encode(JwtEncoderParameters.from(jwtClaimsSet)).getTokenValue();: ์์ฑํ ํด๋ ์ ์
์ ์ด์ฉํ์ฌ ์๋ก์ด ํ ํฐ์ ์ธ์ฝ๋ฉํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ํด๋น ํ ํฐ์ ๊ฐ์ ๊ฐ์ ธ์ ๋ณ์์ ์ ์ฅํฉ๋๋ค.return Map.of("token", token);: ์์ฑ๋ ํ ํฐ์ "token" ํค์ ํจ๊ป ๋งต ํํ๋ก ๋ฐํํฉ๋๋ค. ์ด ํ ํฐ์ ํด๋ผ์ด์ธํธ์์ ์๋ก์ด ์ธ์ฆ์ ์ฌ์ฉ๋ ๊ฒ์
๋๋ค.MemberEdit.jsx(React)
function handleClickSave() {
axios
.put("/api/member/modify", { ...member, oldPassword })
.then((res) => {
toast({
status: "success",
description: "ํ์ ์ ๋ณด๊ฐ ์์ ๋์์ต๋๋ค.",
position: "top",
});
account.login(res.data.token);
navigate(`/member/${id}`);
})
~~~
}
account.login(res.data.token) : ์์ฒญ์ด ์ฑ๊ณตํ๋ฉด ์๋ฒ๋ก๋ถํฐ ๋ฐ์ ํ ํฐ์ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์๋ฅผ ๋ก๊ทธ์ธ ์ํ๋ก ์ค์ ํ๋ ๊ฒ์
๋๋ค. ์ฆ, ํด๋ผ์ด์ธํธ๊ฐ ์๋ก์ด ํ ํฐ์ ๋ฐ์ผ๋ฉด ํด๋น ํ ํฐ์ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์๋ฅผ ๋ก๊ทธ์ธํ๊ณ , ์ด๋ฅผ ํตํด ์ฌ์ฉ์๋ ๋ก๊ทธ์ธ ์ํ๋ฅผ ์ ์งํ๋ฉฐ ์๋น์ค๋ฅผ ๊ณ์ ์ด์ฉํ ์ ์์ต๋๋ค.