Grails에서 Service 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에서 Fackbook OAuth를 사용해서 인증하기
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")
}
}