Clean Code that Works.

스프링 처럼 DI를 받을 수 있는데...

FBUserDetailService를 di 받아서 사용할 경우 일반적으로 
def fbUserDetailService 이런 형식의 카멜 표기법으로 정의하는것으로 생각한다.

하지만 되지 않아!!!!!

http://grails.org/doc/latest/guide/services.html#dependencyInjectionServices 

저기 문서의 가운데를 보면

NOTE: Normally the property name is generated by lower casing the first letter of the type. For example, an instance of the BookService class would map to a property named bookService.

To be consistent with standard JavaBean conventions, if the first 2 letters of the class name are upper case, the property name is the same as the class name. For example, the property name of theJDBCHelperService class would be JDBCHelperService, not jDBCHelperService or jdbcHelperService.

See section 8.8 of the JavaBean specification for more information on de-capitalization rules.



JDBCHelperService를 DI 받을려면 JDBCHelperService로 작명을 해주어야 한다고 한다.

뭐 그럼 나도 사용할 때

def FBUserDetailService 로 하면 됨 @_@

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")

}

}