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