67 |
Grails Spring Security Facebook Plugin
68 |
69 |
70 |
71 | | Version |
72 | @VERSION@ |
73 | |
74 |
75 | | Grails Version |
76 | 3.0.0 > * |
77 |
78 |
79 | | Author |
80 | Igor Artamonov |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
Current Documentation
88 |
93 |
94 |
Documentation (version 0.17, for Grails 2.x)
95 |
98 |
99 |
100 |
101 |
109 |
110 |
Download Source
111 |
112 | You can download this project in either
113 | zip or
114 | tar formats.
115 |
116 |
You can also clone the project with Git by running:
117 |
$ git clone git://github.com/grails-plugins/grails-spring-security-facebook
118 |
119 |
120 |
121 |
122 |
135 |
136 |
137 |
--------------------------------------------------------------------------------
/src/main/groovy/com/the6hours/grails/springsecurity/facebook/FacebookAuthRedirectFilter.groovy:
--------------------------------------------------------------------------------
1 | package com.the6hours.grails.springsecurity.facebook
2 |
3 | import groovy.transform.CompileStatic
4 |
5 | import javax.servlet.FilterChain
6 | import javax.servlet.ServletException
7 | import javax.servlet.ServletRequest
8 | import javax.servlet.ServletResponse
9 | import javax.servlet.http.HttpServletRequest
10 | import javax.servlet.http.HttpServletResponse
11 |
12 | import grails.web.mapping.LinkGenerator
13 | import org.springframework.security.core.Authentication
14 | import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter
15 | import org.springframework.security.web.util.UrlUtils
16 | import org.springframework.security.web.util.matcher.RequestMatcher
17 | import org.springframework.util.Assert
18 |
19 | /**
20 | *
21 | * @author Igor Artamonov (http://igorartamonov.com)
22 | * @since 19.09.12
23 | */
24 | @CompileStatic
25 | class FacebookAuthRedirectFilter extends AbstractAuthenticationProcessingFilter {
26 |
27 | FacebookAuthUtils facebookAuthUtils
28 | LinkGenerator linkGenerator
29 | RequestMatcher redirectFromMatcher
30 |
31 | String redirectFromUrl
32 | String redirectToUrl
33 |
34 | FacebookAuthRedirectFilter(String defaultFilterProcessesUrl) {
35 | super(defaultFilterProcessesUrl)
36 | this.redirectToUrl = defaultFilterProcessesUrl
37 | }
38 |
39 | @Override
40 | void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
41 | if (redirectFromMatcher.matches((HttpServletRequest)request)) {
42 | ((HttpServletResponse)response).sendRedirect(
43 | facebookAuthUtils.prepareRedirectUrl(absoluteRedirectUrl, facebookAuthUtils.requiredPermissions))
44 | }
45 | else {
46 | super.doFilter(request, response, chain)
47 | }
48 | }
49 |
50 | @Override
51 | Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
52 | String code = request.getParameter('code')
53 | if (!code) {
54 | throw new InvalidRequestException("Request is empty")
55 | }
56 |
57 | logger.debug("Got 'code' from Facebook. Process authentication using this code")
58 | authenticationManager.authenticate new FacebookAuthToken(code: code, uid: -1L, redirectUri: getAbsoluteRedirectUrl())
59 | }
60 |
61 | String getAbsoluteRedirectUrl() {
62 | linkGenerator.link(uri: redirectToUrl, absolute: true)
63 | }
64 |
65 | void setRedirectFromUrl(String redirectFromUrl) {
66 | this.redirectFromUrl = redirectFromUrl
67 | this.redirectFromMatcher = new FriendlyFilterProcessUrlRequestMatcher(redirectFromUrl)
68 | }
69 |
70 | //original matcher from Spring Security is private
71 | static final class FriendlyFilterProcessUrlRequestMatcher implements RequestMatcher {
72 | public final String filterProcessesUrl
73 |
74 | FriendlyFilterProcessUrlRequestMatcher(String filterProcessesUrl) {
75 | Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified")
76 | Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), "$filterProcessesUrl isn't a valid redirect URL")
77 | this.filterProcessesUrl = filterProcessesUrl
78 | }
79 |
80 | boolean matches(HttpServletRequest request) {
81 | String uri = request.requestURI
82 | int pathParamIndex = uri.indexOf(';')
83 |
84 | if (pathParamIndex > 0) {
85 | // strip everything after the first semi-colon
86 | uri = uri.substring(0, pathParamIndex)
87 | }
88 |
89 | if (request.contextPath) {
90 | StringBuilder expectedPath = new StringBuilder()
91 | expectedPath.append(request.contextPath).append(filterProcessesUrl)
92 | return uri.endsWith(expectedPath.toString())
93 | }
94 | return uri.endsWith(filterProcessesUrl)
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/main/groovy/com/the6hours/grails/springsecurity/facebook/FacebookAuthCookieTransparentFilter.groovy:
--------------------------------------------------------------------------------
1 | package com.the6hours.grails.springsecurity.facebook
2 |
3 | import groovy.transform.CompileStatic
4 |
5 | import javax.servlet.FilterChain
6 | import javax.servlet.ServletRequest
7 | import javax.servlet.ServletResponse
8 | import javax.servlet.http.Cookie
9 | import javax.servlet.http.HttpServletRequest
10 | import javax.servlet.http.HttpServletResponse
11 |
12 | import org.springframework.context.ApplicationEventPublisher
13 | import org.springframework.context.ApplicationEventPublisherAware
14 | import org.springframework.security.authentication.AuthenticationManager
15 | import org.springframework.security.authentication.BadCredentialsException
16 | import org.springframework.security.core.Authentication
17 | import org.springframework.security.core.context.SecurityContextHolder
18 | import org.springframework.web.filter.GenericFilterBean
19 |
20 | /**
21 | * TODO
22 | *
23 | * @since 14.10.11
24 | * @author Igor Artamonov (http://igorartamonov.com)
25 | */
26 | @CompileStatic
27 | class FacebookAuthCookieTransparentFilter extends GenericFilterBean implements ApplicationEventPublisherAware {
28 |
29 | ApplicationEventPublisher applicationEventPublisher
30 | FacebookAuthUtils facebookAuthUtils
31 | AuthenticationManager authenticationManager
32 | String logoutUrl = '/j_spring_security_logout'
33 | String forceLoginParameter
34 | String filterProcessUrl
35 |
36 | void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
37 | HttpServletRequest request = (HttpServletRequest)req
38 | HttpServletResponse response = (HttpServletResponse)res
39 | String url = request.requestURI.substring(request.contextPath.length())
40 | logger.debug("Processing url: $url")
41 | if (url != logoutUrl &&
42 | (!SecurityContextHolder.context.authentication ||
43 | (forceLoginParameter && request.getParameter(forceLoginParameter) == 'true'))) {
44 | logger.debug("Applying facebook auth filter")
45 | assert facebookAuthUtils
46 | Cookie cookie = facebookAuthUtils.getAuthCookie(request)
47 | if (cookie) {
48 | if (processCookie(cookie, request, response, chain)) {
49 | return
50 | }
51 | }
52 | else {
53 | logger.debug("No auth cookie")
54 | }
55 | }
56 | else {
57 | logger.debug("SecurityContextHolder not populated with FacebookAuthToken token, as it already contained: $SecurityContextHolder.context.authentication");
58 | }
59 |
60 | //when not authenticated, dont have auth cookie or bad credentials
61 | chain.doFilter(request, response)
62 | }
63 |
64 | protected boolean processCookie(Cookie cookie, HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
65 | try {
66 | FacebookAuthToken token = facebookAuthUtils.build(cookie.value)
67 | if (!token) {
68 | return false
69 | }
70 |
71 | Authentication authentication
72 | try {
73 | authentication = authenticationManager.authenticate(token)
74 | }
75 | catch (Throwable t) {
76 | logger.warn("Error during authentication. Skipping. Message: $t.message")
77 | }
78 | if (authentication?.authenticated) {
79 | // Store to SecurityContextHolder
80 | SecurityContextHolder.context.authentication = authentication
81 |
82 | if (logger.debugEnabled) {
83 | logger.debug("SecurityContextHolder populated with FacebookAuthToken: '$SecurityContextHolder.context.authentication'");
84 | }
85 | try {
86 | chain.doFilter(request, response)
87 | return true
88 | }
89 | finally {
90 | SecurityContextHolder.context.authentication = null
91 | }
92 | }
93 | }
94 | catch (BadCredentialsException e) {
95 | logger.info("Invalid cookie, skip. Message was: $e.message")
96 | }
97 |
98 | false
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/docs/customization.adoc:
--------------------------------------------------------------------------------
1 | == Customization
2 |
3 | === How it works
4 |
5 | If you need to add some specific logic to default plugin behaviour you have to create your own
6 | service called `FacebookAuthService`. Plugin will check for know methods of this service, and if
7 | they're exist - use them instead of own.
8 |
9 | It's some kind of extending an abstract class. You don't need to create all methods, just what you need.
10 |
11 | Used objects:
12 |
13 | *