Spring Security 단일 프로젝트 권한 설계 프로 세 스 분석
현재 자바 웹 의 세계 에서 스프링 은 하나의 강호 라 고 할 수 있다.마이크로 서비스 가 도래 함 에 따라 스프링 클 라 우 드 는 자바 프로그래머 가 반드시 익 혀 야 할 프레임 워 크 라 고 할 수 있다.알 리 마저 스프링 클 라 우 드 를 위해 기원 을 썼 다.예 를 들 어 유명한 Nacos)는 Spring 의 친아들 로 서 SpringSecurity 는 마이크로 서비스의 생태 에 잘 적응 했다.당신 은 Oauth 와 매우 간편 하 게 결합 하여 인증 센터 서 비 스 를 할 수 있 습 니 다.본 고 는 가장 간단 한 단일 프로젝트 부터 시작 하여 Security 를 점차적으로 파악 하고 자 한다.공식 문서
준비 하 다.
나 는 간단 한 demo 를 준비 했다.구체 적 인 코드 는 문 말 에 넣 을 것 이다.미리 말씀 드 리 지만 본 demo 는 JWT 를 사용 하지 않 았 습 니 다.왜냐하면 저 는 token 의 유 지 를 서버 에 두 고 기한 이 지난 시간 을 더 잘 유지 하고 싶 기 때 문 입 니 다.(물론 앞으로 마이크로 서비스 인증 센터 형식 이 라면 JWT 도 만 료 기간 을 편리 하 게 유지 하고 지나치게 논의 하지 않 을 수 있 습 니 다)Security+JWT 간이 입문 에 대해 알 고 싶다 면 도장 을 찍 어 주 십시오.
본 항목 의 구 조 는 다음 과 같다.
한편,본 데 모 는 MybatisPlus,lombok 을 사용 했다.
핵심 코드
먼저 두 가지 유형 을 실현 해 야 한다.하 나 는 User Details 의 실현 유형 인 Security User 이 고 하 나 는 User Details Service 의 실현 유형 인 Security User Service 이다.
**
* Security User
* */
@Data
public class SecurityUser implements UserDetails {
@Autowired
private SysRoleService sysRoleService;
// ( username SysUser loginName )
private String username;
//
private String password;
// id
private SysUser sysUser;
//
private List<SysMenu> sysMenuList;
/** */
public SecurityUser(SysUser sysUser){
this.username = sysUser.getLoginName();
this.password = sysUser.getPassword();
this.sysUser = sysUser;
}
public SecurityUser(SysUser sysUser,List<SysMenu> sysMenuList){
this.username = sysUser.getLoginName();
this.password = sysUser.getPassword();
this.sysMenuList = sysMenuList;
this.sysUser = sysUser;
}
/** */
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<>();
for(SysMenu menu : sysMenuList) {
authorities.add(new SimpleGrantedAuthority(menu.getPerms()));
}
return authorities;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
//
@Override
public boolean isAccountNonExpired() {
return true;
}
//
@Override
public boolean isAccountNonLocked() {
return true;
}
//
@Override
public boolean isCredentialsNonExpired() {
return true;
}
//
@Override
public boolean isEnabled() {
return true;
}
}
이 종 류 는 어떤 요청 자의 정 보 를 포함 하고 있 으 며,Security 에 서 는 주체 라 고 부른다.그 중에서 이 방법 은 반드시 실현 되 어야 하 며 사용자 의 구체 적 인 권한 을 얻 을 수 있다.우리 쪽 권한 의 과립 도 는 메뉴 단계 에 이 르 렀 습 니 다.많은 오픈 소스 항목 에서 캐릭터 의 등급 이 아니 라 과립 도가 가 늘 수록 편리 하 다 고 생각 합 니 다.(개인 적 으로...)
/**
* Security UserService
* */
@Service
public class SecurityUserService implements UserDetailsService{
@Autowired
private SysUserService sysUserService;
@Autowired
private SysMenuService sysMenuService;
@Autowired
private HttpServletRequest httpServletRequest;
@Override
public SecurityUser loadUserByUsername(String loginName) throws UsernameNotFoundException {
LambdaQueryWrapper<SysUser> condition = Wrappers.<SysUser>lambdaQuery().eq(SysUser::getLoginName, loginName);
SysUser sysUser = sysUserService.getOne(condition);
if (Objects.isNull(sysUser)){
throw new UsernameNotFoundException(" !");
}
Long projectId = null;
try{
projectId = Long.parseLong(httpServletRequest.getHeader("projectId"));
}catch (Exception e){
}
SysMenuModel sysMenuModel;
if (sysUser.getUserType()){
sysMenuModel = new SysMenuModel();
}else {
sysMenuModel = new SysMenuModel().setUserId(sysUser.getId());
}
sysMenuModel.setProjectId(projectId);
List<SysMenu> menuList = sysMenuService.getList(sysMenuModel);
return new SecurityUser(sysUser,menuList);
}
}
이 종 류 는 유일한 방법 인 loadUserByUsername 을 실현 하여 특정한 사용자 의 모든 권한 을 얻 고 주 체 를 생 성 할 수 있 으 며 뒤의 filter 에서 그의 역할 을 볼 수 있 습 니 다.설정 과 filter 를 보기 전에 한 가지 설명 이 필요 합 니 다.이러한 제공 방법 은 사용자 가 로그 인하 지 않 거나 token 이 실 효 된 상황 에서 통일 적 으로 되 돌아 갈 수 있 습 니 다.
@Component
public class SecurityAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = 1L;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,"token , ");
}
}
ok.다음은 설정 을 보고 웹 보안 Configurer Adapter 의 Security Config 류 를 실현 합 니 다.특히 이 demo 는 앞 뒤 가 분 리 된 전제 에서 쓴 것 이기 때문에 너무 많은 방법 을 실현 할 수 있 습 니 다.사실은 이 종 류 는 세 가지 방법 을 실현 할 수 있 습 니 다.
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
SecurityAuthenticationEntryPoint securityAuthenticationEntryPoint;
@Autowired
SecurityFilter securityFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// csrf
.csrf().disable()
//
.exceptionHandling().authenticationEntryPoint(securityAuthenticationEntryPoint).and()
//Session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
//
.authorizeRequests()
.antMatchers("/login/login").permitAll()
.antMatchers("/login/register").permitAll()
.antMatchers("/login/logout").permitAll()
.anyRequest().authenticated();
http
.addFilterBefore(securityFilter, UsernamePasswordAuthenticationFilter.class);
}
}
이상 처 리 는 바로 위의 클래스 입 니 다.Session 의 몇 가지 관리 방식 은 제 가 그 Security+JWT 의 글 에서 도 설명 을 했 습 니 다.비교적 간단 합 니 다.그 다음 에 검증 되 지 않 은 로그 인 경 로 를 몇 개 하고 나머지 는 저희 아래 의 filter 를 거 쳐 야 합 니 다.
@Slf4j
@Component
public class SecurityFilter extends OncePerRequestFilter {
@Autowired
SecurityUserService securityUserService;
@Autowired
SysUserService sysUserService;
@Autowired
SysUserTokenService sysUserTokenService;
/**
*
* */
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
FilterChain filterChain) throws ServletException, IOException {
log.info(" :{}",httpServletRequest.getRequestURL());
try {
final String token = httpServletRequest.getHeader("token");
LambdaQueryWrapper<SysUserToken> condition = Wrappers.<SysUserToken>lambdaQuery().eq(SysUserToken::getToken, token);
SysUserToken sysUserToken = sysUserTokenService.getOne(condition);
if (Objects.nonNull(sysUserToken)){
SysUser sysUser = sysUserService.getById(sysUserToken.getUserId());
if (Objects.nonNull(sysUser)){
SecurityUser securityUser = securityUserService.loadUserByUsername(sysUser.getLoginName());
//
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
//
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}catch (Exception e){
log.error(" :{}", Arrays.toString(e.getStackTrace()));
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
사용자 의 로그 인 여 부 를 판단 하 는 것 은 데이터베이스 에서 만 료 되 지 않 은 token 이 있 는 지 확인 하 는 것 입 니 다.존재 하면 주체 정 보 를 프로젝트 의 메모리 에 넣 습 니 다.특히 모든 요청 체인 이 끝나 면 Security ContextHolder.getContext()의 데 이 터 는 clear 되 기 때문에 요청 할 때마다 set 가 필요 합 니 다.이상 은 Security 핵심 생 성 을 완 료 했 습 니 다.업무 코드 가 메모리 의 주체 정 보 를 쉽게 얻 기 위해 저 는 사용자 정 보 를 얻 는 방법 을 특별히 추 가 했 습 니 다.
/**
* Security
* @author pjjlt
* */
public class SecurityUserUtil {
public static SysUser getCurrentUser(){
SecurityUser securityUser = (SecurityUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (Objects.nonNull(securityUser) && Objects.nonNull(securityUser.getSysUser())){
return securityUser.getSysUser();
}
return null;
}
}
비 즈 니스 코드이상 은 Security 핵심 코드 입 니 다.로그 인 과 특정한 인터페이스의 권한 접근 테스트 등 두 개의 업무 코드 를 간단하게 추가 합 니 다.
만물 의 근원 등록 등극
우선,filter 에 의 해 차단 되 지 않 는 세 가지 방법 으로 등록,로그 인,로그아웃 을 했 습 니 다.저 는 모두 moudle.contrller.Login Controller 라 는 경로 에 썼 습 니 다.등록 은 말 할 필요 도 없 이 insert User 의 방법 입 니 다.판단 을 잘 하면 됩 니 다.비밀 번 호 는 AES 를 통 해 비밀 번 호 를 추가 합 니 다.
로그 인 코드 를 보 니 contrller 층 은 말 하지 않 겠 습 니 다.어차피 검사 입 니 다.
/**
* , ,
* */
@Override
@Transactional(rollbackFor = Exception.class)
public JSONObject login(SysUserModel sysUserModel) throws Exception{
JSONObject result = new JSONObject();
//1. 、 、
Wrapper<SysUser> sysUserWrapper = Wrappers.<SysUser>lambdaQuery()
.eq(SysUser::getLoginName,sysUserModel.getLoginName()).or()
.eq(SysUser::getEmail,sysUserModel.getEmail());
SysUser sysUser = baseMapper.selectOne(sysUserWrapper);
if (Objects.isNull(sysUser)){
throw new Exception(" !");
}
String password = CipherUtil.encryptByAES(sysUserModel.getPassword());
if (!password.equals(sysUser.getPassword())){
throw new Exception(" !");
}
if (sysUser.getStatus()){
throw new Exception(" !");
}
// 2.
sysUser.setLoginIp(ServletUtil.getClientIP(request));
sysUser.setLoginDate(LocalDateTime.now());
baseMapper.updateById(sysUser);
// 3. token,
String token = UUID.fastUUID().toString().replace("-","");
LocalDateTime expireTime = LocalDateTime.now().plusSeconds(expireTimeSeconds);
SysUserToken sysUserToken = new SysUserToken()
.setToken(token).setUserId(sysUser.getId()).setExpireTime(expireTime);
sysUserTokenService.save(sysUserToken);
result.putOpt("token",token);
result.putOpt("expireTime",expireTime);
return result;
}
먼저 사용자 가 존재 하 는 지,로그 인 비밀번호 가 정확 한 지 확인 한 다음 에 token 을 패키지 합 니 다.특히 저 는 데이터베이스(sys UserToken)에서 사용자 가 로그 인 한 token 을 가 져 오지 않 았 습 니 다.그리고 만 료 된 시간 형식 을 업데이트 하여 로그 인 을 하 는 것 이 아니 라 로그 인 할 때마다 새로운 token 을 얻 었 습 니 다.그러면 여러 단 계 를 로그 인 할 수 있 습 니 다.후기 에는 계 정 로그 인 수량 에 대한 통제 도 할 수 있다.그리고 라 이브 러 리 에 존재 하 는 token 을 삭제 합 니 다.
/**
* , token
* */
@Override
public void logout() throws Exception{
String token = httpServletRequest.getHeader("token");
if (Objects.isNull(token)){
throw new LoginException("token ",ResultEnum.LOGOUT_ERROR);
}
LambdaQueryWrapper<SysUserToken> sysUserWrapper = Wrappers.<SysUserToken>lambdaQuery()
.eq(SysUserToken::getToken,token);
baseMapper.delete(sysUserWrapper);
}
권한 검증여기 서 저 는 두 개의 계 정 을 지 켰 습 니 다.하 나 는 슈퍼 관리자 majian 이 고 모든 권한 을 가지 고 있 습 니 다.하 나 는 일반인pjjlt,권한 만 있 습 니 다.인터페이스 에 접근 하 는 효 과 를 보 겠 습 니 다.
우리 가 방문 한 인 터 페 이 스 는 moudle.controller.LoginController 경로 아래 에 있 습 니 다.
@PreAuthorize("hasAnyAuthority('test')")
@GetMapping("test")
public String test(){
return "test";
}
그 중에서 hasAny Authority('test')는 권한 코드 입 니 다.서로 다른 계 정 으로 접근 하 는 것 을 모 의 하 는 것 은 헤더 의 token 값 을 바 꾸 는 것 입 니 다.바로 로그 인 단계 에서 전단 의 token 으로 돌아 가 는 것 입 니 다.
우선 슈퍼 관리자 인증 입 니 다.
그리고 일반 관리자 방문.
이어서 로그 인 하지 않 았 습 니 다(token 이 존재 하지 않 거나 만 료 되 었 습 니 다).
demo 주소
https://github.com/majian1994/easy-file-back
종결 어
본 고 는 간단하게 설명 했다.주로 Security 와 관련 된 것 을 구체 적 으로 캐릭터 를 실현 하 는 세 가지 요소 이다.사용자,역할,권한(메뉴)은 제 코드 를 볼 수 있 고 테스트 를 마 쳤 습 니 다.문서 관리 시스템 을 써 서 저희 회사 가 인터페이스 문 서 를 잘 관리 하도록 도와 주 려 고 했 는데 한 파트너 가 좋 은 오픈 소스 를 찾 았 습 니 다.그래서 이 코드 는 나의 작은 demo 가 되 었 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
thymeleaf로 HTML 페이지를 동적으로 만듭니다 (spring + gradle)지난번에는 에서 화면에 HTML을 표시했습니다. 이번에는 화면을 동적으로 움직여보고 싶기 때문에 입력한 문자를 화면에 표시시키고 싶습니다. 초보자의 비망록이므로 이상한 점 등 있으면 지적 받을 수 있으면 기쁩니다! ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.