Spring Security 동적 권한 관리 실현

19659 단어 자바
내 가 이해 하 는 동적 권한 은 바로 RBAC(Role-Based Access Control)이다.캐릭터 를 사용자 정의 할 수 있 고 캐릭터 가 어떤 URL 에 접근 할 수 있 는 지 설정 할 수 있 습 니 다.그리고 서로 다른 캐릭터 에 게 서로 다른 캐릭터 를 설정 합 니 다.
Spring Security 를 왜 써 요?Spring Security 는 Shiro 기반 이 라 고 들 었 습 니 다.Shiro 는 안 써 봤 어 요.Spring Security 를 사용 하 는 이 유 는 안전 하기 때문이다.쓸데없는 소리!csrf 등의 공격 을 방어 할 수 있 기 때 문 입 니 다.사실 현재 크롬 브 라 우 저의 동원 정책 은 이미 매우 좋아 서 csrf 를 생각 하 는 것 도 그리 쉽 지 않다.Spring Security 가 얼마나 막 을 수 있 을 지 모 르 겠 어 요.어쨌든 아무것도 못 하 는 것 보다.XSS 는 여러분 이 필 터 를 잘 해 주시 면 됩 니 다.예 를 들 어 Spring MVC 는 일부 걸 러 낼 수 있 고 페이지 는태그 로 출력 할 수 있 습 니 다.주제 에서 벗 어 났 구나,사실 나 는 X 인 척 하고 싶 었 을 뿐 이 야.
봄 보안 식 언어 주석 에서 사용 할 사용자 정의 메 서 드 를 만 드 는 방법 참고
Spring Security 로 동적 권한 관 리 를 실현 하려 고 했 는데 이번 에는 해결 되 어 소원 을 이 루 었 습 니 다.쓸데없는 소리 하지 말고 스프링 시 큐 리 티 를 쓴다 고 가정 해 봐.할 줄 모 르 시 면 공식 문 서 를 많이 보 세 요.Spring 의 문 서 는 상당히 잘 썼 는데,네가 인내심 을 가지 고 보지 못 할 까 봐 걱정 된다.예 를 들 어 당신 은 Spring MVC 를 사용 한 적 이 있 고 이미 Spring MVC 프로젝트 가 있 습 니 다.지금 당신 의 Spring MVC 프로젝트 에 Spring Security 를 통합 하려 면 어떻게 합 니까?이 tutorial:Hello Spring MVC Security Java Config 는 한 마디 만 하고 싶 습 니 다.이전에 Spring MVC 의 프로필 은 getServletConfigClasses 라 는 방법 에 있 었 는데,지금 은 getRoot ConfigClasses 방법 으로 옮 겨 야 합 니 다.왜 냐 면 문서 에 도 있 고 아래 주석 에 도 있 습 니 다.주석 은 모두 공식 문서 에서 나 왔 습 니 다.
/** * it’s even the recommended way to set up your Spring MVC application */
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class>[] getRootConfigClasses() {
        return new Class[] { WebConfig.class };
        // The @ComponentScan is loading all configuration within the same package (and child packages) as RootConfiguration. Since SecurityConfig is in this package, it will be loaded with our existing setup and there is nothing more to do.
    }

    @Override
    protected Class>[] getServletConfigClasses() {
        return null;
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }

}

사실 간단 해.클래스 를 사용자 정의 합 니 다.클래스 에 방법 을 추가 합 니 다:
@Component("mySecurityService")
public class MySecurityService {
    public boolean hasPermission(Authentication authentication, Object foo) {
        System.out.println("  ");
        Collection extends GrantedAuthority> authorities = authentication.getAuthorities(); Iterator extends GrantedAuthority> iterator = authorities.iterator(); while(iterator.hasNext()){ GrantedAuthority ga = iterator.next(); if("ROLE_ADMIN".equals(ga.getAuthority())){ return true; } } return false; } }

그리고 사용 할 곳 에 하나@PreAuthorize("@mySecurityService.hasPermission(authentication, #model)")를 더 하면 OK.
    @PreAuthorize("@mySecurityService.hasPermission(authentication, #model)")
    @RequestMapping(value = { "/testMethod" }, method = RequestMethod.GET)
    public String testMethodSecurity(ModelMap model) {
        model.addAttribute("greeting", "   。         admin       ");
        return "welcome";
    }

여기 authentication.getAuthorities()에 주의 하 십시오.이것 을 얕 보지 마라,이것 은 정말 까닭 이 있다!
Spring Security 는 모두 두 개의 큰 덩어리 로 나 뉘 는데 그것 이 바로 anthentication 과 authorization 이다.스프링 시 큐 리 티 처음 배 울 때 두 군데 다 걸 려!왜?
Spring Security 에는 기본 anthentication 이 있 습 니 다.로그 인 용 form 폼 을 만 드 는 데 도움 을 줄 것 입 니 다.알 이 아 픈 것 은 이 form 폼 은 username 과 password 두 개의 인자 만 있 습 니 다.문제 가 생 겼 습 니 다.인증 코드 를 추가 하려 면 어떻게 해 야 합 니까?뿐만 아니 라 기본 authentication 은 하 드 인 코딩 의 역할 을 자동 으로 주입 합 니 다.바로 당신 이 프로필 에 설정 한 ROLE 입 니 다.USER,ROLE_애 민 아,잠깐 만.캐릭터 를 사용자 정의 하고 싶 습 니 다.동적 캐릭터 를 원 합 니 다.어떻게 하 죠?렉 걸 렸 지?
기본 authentication 을 사용 하면 authentication.getAuthorities();받 은 캐릭터 는 고정 되 어 있 습 니 다.
authorization 어디 에 걸 릴 거 예요?문서 에 들 어 있 는 예 는@PreAuthorize("hasRole('ADMIN') AND hasRole('DBA')")보 셨 죠?여기 있 는 ROLE 는 동적 인 것 이 아니 라 고정 적 인 것 입 니 다.
그래서 당신 에 게 필요 한 것 은@PreAuthorize("canIAccess(authorities)"입 니 다.즉,decide 방법 을 사용자 정의 하고 동적 캐릭터 를 이 decide 방법 에 전달 하 는 것 입 니 다!
어떻게?
로그 인(authenticate)할 때 인증 코드 를 가 져 오 려 면 form 폼 을 사용자 정의 해 야 합 니 다.동적 역할 을 하려 면 로그 인 에 성공 할 때 사용자 의 역할 정 보 를 db 에서 찾 아서 Spring 의 특정한 유형 에 주입 해 야 합 니 다.구체 적 인 종 류 는 잊 어 버 렸 습 니 다.문서 에 언급 되 어 있 습 니 다.문서'Architecture and Implementation'절 을 자세히 봐 야 합 니 다.그리고 decide 방법 에서 이전에 주 입 된 캐릭터 정 보 를 얻 습 니 다.
말 하기는 쉬 워 도 실행 하기는 그리 쉽 지 않다!
예 를 들 어 인증 코드 인 자 는 인증(authenticate)할 때 어떻게 가 져 옵 니까?먼저 captcha 를 session 에 저장 하 라 고 말 할 수 있 습 니 다.인증 할 때 리퀘스트 상 대 를 받 으 면 되 는 거 아니에요?관건 은 네가 가 져 온 거 야?!
인증 할 때 request 를 받 기 가 너무 쉽 지 않 습 니 다.
우선 BeanPost Processor 에 사용자 정의 인증 원 을 등록 해 야 합 니 다:MyWebAuthentication Details Source
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String name) throws BeansException {
        if (bean instanceof UsernamePasswordAuthenticationFilter) {
            MyWebAuthenticationDetailsSource myWebAuthenticationDetailsSource = new MyWebAuthenticationDetailsSource();
            ((UsernamePasswordAuthenticationFilter) bean)
                    .setAuthenticationDetailsSource(myWebAuthenticationDetailsSource);
        }
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
// System.out.println(bean.getClass());
        return bean;
    }
}

MyWebAuthentication Details Source 의 buildDetails 방법 에서 사용자 정의 상세 인증 클래스 MyWebAuthentication Details 를 되 돌려 줍 니 다.
public class MyWebAuthenticationDetailsSource extends WebAuthenticationDetailsSource {

    @Override
    public WebAuthenticationDetails buildDetails(HttpServletRequest context) {
        return new MyWebAuthenticationDetails(context);
    }

}

상세 인증 클래스 에서 Spring 이 자동 으로 request 대상 을 주입 하도록 합 니 다.
public class MyWebAuthenticationDetails extends WebAuthenticationDetails {

    /** * serialVersionUID */
    private static final long serialVersionUID = 1435615659672216808L;
    private HttpServletRequest request;

    public MyWebAuthenticationDetails(HttpServletRequest request) {
        super(request);
        this.request = request;
    }

    public HttpServletRequest getRequest() {
        return request;
    }

    public void setRequest(HttpServletRequest request) {
        this.request = request;
    }

}

마지막 으로 사용자 정의 인증 에서 MyWebAuthentication Details 를 받 아 request 를 받 습 니 다.
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
    @Autowired
    private CustomUserService userService;

    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        MyWebAuthenticationDetails myDetails = (MyWebAuthenticationDetails) authentication.getDetails();
        HttpServletRequest request = myDetails.getRequest();
        String captcha = request.getParameter("captcha");
        HttpSession session = request.getSession();
        String realCaptcha = (String) session.getAttribute("captcha");
        if(!realCaptcha.equals(captcha)){
            throw new BadCredentialsException("     ");
        }
        String username = authentication.getName();
        String password = (String) authentication.getCredentials();

        CustomUser user = userService.loadUserByUsername(username);

        // http://docs.spring.io/spring-security/site/faq/faq.html#faq-extra-login-fields
        if (user == null || !user.getUsername().equalsIgnoreCase(username) || !password.equals(user.getPassword())) {
            throw new BadCredentialsException("        ");
        } 
        Collection extends GrantedAuthority> authorities = user.getAuthorities();
        return new UsernamePasswordAuthenticationToken(user, password, authorities);
    }

    public boolean supports(Class> arg0) {
        return true;
    }
}

이 CustomUserService 는 저희 가 사용자 정의 UserDetails Service 입 니 다.바로 이런 종 류 를 통 해 사용자 정 보 를 db 에서 찾 아 내 는 것 이다.편 의 를 위해 서 나 는 직접 service 에서 죽 었 다 고 썼 다.dao 를 정의 하기 가 귀 찮 고 db 에서 찾 았 다.
@Service
public class CustomUserService implements UserDetailsService{
// @Autowired
// private UserDAOImpl userDao;

   public CustomUser loadUserByUsername(String username) throws UsernameNotFoundException {
// return userDao.loadUserByUsername(username);
       CustomUser user = new CustomUser();
       if("bill".equals(username)){
           user.setFirstName("kb");
           user.setLastName("gc");
           user.setUsername("bill");
           user.setPassword("ff9830c42660c1dd1942844f8069b74a");
           CustomRole userRole = new CustomRole();
           userRole.setName("ROLE_USER");
           List roles = new ArrayList();
           roles.add(userRole);
           user.setAuthorities(roles);
       } else if("admin".equals(username)){
           user.setFirstName("kb2");
           user.setLastName("gc2");
           user.setUsername("admin");
           user.setPassword("ff9830c42660c1dd1942844f8069b74a");
           List roles = new ArrayList();
           CustomRole userRole = new CustomRole();
           userRole.setName("ROLE_USER");
           roles.add(userRole);
           CustomRole adminRole = new CustomRole();
           adminRole.setName("ROLE_ADMIN");
           roles.add(adminRole);
           user.setAuthorities(roles);
       } else if("dba".equals(username)){
           user.setFirstName("kb3");
           user.setLastName("gc3");
           user.setUsername("dba");
           user.setPassword("ff9830c42660c1dd1942844f8069b74a");
           List roles = new ArrayList();
           CustomRole userRole = new CustomRole();
           userRole.setName("ROLE_USER");
           roles.add(userRole);
           CustomRole adminRole = new CustomRole();
           adminRole.setName("ROLE_ADMIN");
           roles.add(adminRole);
           CustomRole dbaRole = new CustomRole();
           dbaRole.setName("ROLE_DBA");
           roles.add(dbaRole);
           user.setAuthorities(roles);
       } else{
           user = null;
       }
       return user;
   }
}

커 스 텀 롤 도 우리 가 정의 한 롤 이다.이렇게 하면 사용자 정의 ROLE 에 다른 필드 를 추가 할 수 있 습 니 다.여기 서 name 필드 만 정의 합 니 다.
public class CustomRole implements GrantedAuthority{
    private String name;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAuthority() {
        return this.name;
    }
}

사용자 정의 인증 은 Custom Authentication Provider 의 authenticate 방법 에서 이 루어 집 니 다.앞 에 그렇게 많은 일 을 했 는데 고작 request 하나 때문에여기 maimapi 라 는 말 이 있 습 니 다.
authenticate 방법의 마지막 return new UsernamePassword AuthenticationToken(user,password,authorities);authorities 를 Spring 의 어떤 종류 에 주입 시 켰 습 니 다.그래서 MySecurity Service 의 hasPermission 방법 에서 저 희 는 authentication 을 받 아 authorities 를 받 을 수 있 습 니 다.
@Component("mySecurityService")
public class MySecurityService {
    public boolean hasPermission(Authentication authentication, Object foo) {
        System.out.println("  ");
        Collection extends GrantedAuthority> authorities = authentication.getAuthorities();
        Iterator extends GrantedAuthority> iterator = authorities.iterator();
        while(iterator.hasNext()){
            GrantedAuthority ga = iterator.next();
            if("ROLE_ADMIN".equals(ga.getAuthority())){
                return true;
            }
        }
        return false;
    }
}

우여곡절 이 많 군요.이 maimapi 를 말 해 야 합 니까?

좋은 웹페이지 즐겨찾기