Clean Code that Works.

Grails 2.0에서 spring security 플러그인을 지원 하는데, 인증은 Fackbook을 통해서 하고 인증 정보는 spring security를 사용하는 인증 방법.

1. Grails Filter를 사용해서 인증 url을 필터링함.
2. 인증 url로 요청이 들어오고 인증이 안되어 있을 경우 Facebook을 통해 인증 하도록 함.

참고 URL
Grails Filter :http://grails.org/doc/latest/guide/theWebLayer.html#filters 
Facebook 인증 :  http://developers.facebook.com/docs/authentication/ 


FackbookAuthFilters의 코드(약간 자바 스타일 문법도 섞임)
쭉 보면 login으로 요청을 하는데 code 값이 없거나 미인증된 사용자면 Facebook을 통해서 인증을 하도록 한다.
Facebook에서 인증후 code값을 전달해 주는데, 이 code값을 다시 Fackbook을 통해서 accessToken을 받아 오도록 하고, 
이것을 가지고 사용자의 정보를 가져올 수 있다.

def filters = {

all(uri : '/login/**', uriExclude : '/logout/**') {

before = {

String code = params.code


SecurityContext context = SecurityContextHolder.context

String username = context.authentication.getName()


if (code == null && username.equals('anonymousUser')) {

redirect(url: "https://www.facebook.com/dialog/oauth?client_id=" + clientId + "&redirect_uri="+ redirectUri +"&scope=publish_stream,email")

return false

} else {

if (username.equals('anonymousUser')) {

String accessToken = null;

Integer expires = null;


def http = new HTTPBuilder( 'https://graph.facebook.com/oauth/' )


http.get( path: 'access_token',

query: [client_id: clientId, redirect_uri: redirectUri, client_secret: clientSecret, code: code] ) { resp, reader ->

String result = reader.text

String[] pairs = result.split("&")

for (String pair : pairs) {

String[] kv = pair.split("=")

if (kv.length != 2) {

throw new RuntimeException("Unexpected auth response")

} else {

if (kv[0].equals("access_token")) {

accessToken = kv[1]

}

if (kv[0].equals("expires")) {

expires = Integer.valueOf(kv[1])

}

}

}

}

if (checkAuthByAccessToken(accessToken, expires, request, response)) {

redirect(url: serviceUrl)

return false

}

}

}

}


 
Facebook을 통해서 인증 받은 사용자 정보가 서비스에 존재 하지 않은 사용자일경우 기본 롤로 생성을 하고 만약 있으면 조회를 한 후 스프링 시큐리티 세션에 넣어서 인증정보를 가지고 있도록 한다.

def boolean checkAuthByAccessToken(String accessToken, expires, request, response) {

if (accessToken != null && expires != null) {

def fb = new RESTClient('https://graph.facebook.com/')

def resp = fb.get(path: 'me', query: [ access_token: accessToken])

def email

def password = 'test'

ExampleUser user

if (resp.status == 200) {

email = resp.data.email

user = ExampleUser.findByUsername(email)

if (user == null) {

user = new ExampleUser(username: email, password : password, accountExpired: false, accountLocked: false, enabled: true ).save(flush : true)

Role role = Role.findByAuthority('ROLE_USER')

UserRole.create(user, role, true);

} else {

user = userDetailsService.loadUserByUsername(user.username, true)

}

}


UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(user, password)

request.getSession().setAttribute(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_LAST_USERNAME_KEY, TextEscapeUtils.escapeEntities(user.username));

authRequest.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request))


SecurityContextHolder.context.authentication = authRequest


return true

} else {

throw new RuntimeException("Access token and expires not found")

}

}