Spring Security Core Plugin是Spring的一个强大的权限控制插件,Secure your applications using the powerful Spring Security library quickly and easily
官方插件地址:http://www.grails.org/plugin/spring-security-core
官方使用手册:http://grails-plugins.github.com/grails-spring-security-core/docs/manual/
在grails项目中插入Spring Security Core Plugin。
grails install-plugin spring-security-core
如果你使用intellij IDEA的话就可以很简单的插入插件了,如下图
这时系统就会自动插入插件,插入插件后就可开始配置使用spring security Core插件了
详细使用如下,原文地址:http://grails-plugins.github.com/grails-spring-security-core/docs/manual/guide/23%20Tutorials.html#23.1%20Using%20Controller%20Annotations%20to%20Secure%20URLs
$ grails create-app bookstore $ cd bookstore
$ grails install-plugin spring-security-core
$ grails s2-quickstart com.testapp User Role
You can choose your names for your domain classes and package; these are just examples.
Depending on your database, some domain class names might not be valid, especially those relating to security. Before you create names like "User" or "Group", make sure they are not reserved keywords in your database.
The script creates this User class:
package com.testapp
package test
class User {
transient springSecurityService
String username String password boolean enabled boolean accountExpired boolean accountLocked boolean passwordExpired
static constraints = { username blank: false, unique: true password blank: false }
static mapping = { password column: '`password`' }
Set
def beforeInsert() { encodePassword() }
def beforeUpdate() { if (isDirty('password')) { encodePassword() } }
protected void encodePassword() { password = springSecurityService.encodePassword(password) } }
Earlier versions of the plugin didn't include password encryption logic in the domain class, but it makes the code a lot cleaner.
and this Role class:
package com.testapp
class Role {
String authority
static mapping = { cache true }
static constraints = { authority blank: false, unique: true } }
and a domain class that maps the many-to-many join class, UserRole
:
package com.testapp
import org.apache.commons.lang.builder.HashCodeBuilder
class UserRole implements Serializable {
User user Role role
boolean equals(other) { if (!(other instanceof UserRole)) { return false }
other.user?.id == user?.id && other.role?.id == role?.id }
int hashCode() { def builder = new HashCodeBuilder() if (user) builder.append(user.id) if (role) builder.append(role.id) builder.toHashCode() }
static UserRole get(long userId, long roleId) { find 'from UserRole where user.id=:userId and role.id=:roleId', [userId: userId, roleId: roleId] }
static UserRole create(User user, Role role, boolean flush = false) { new UserRole(user: user, role: role).save(flush: flush, insert: true) }
static boolean remove(User user, Role role, boolean flush = false) { UserRole instance = UserRole.findByUserAndRole(user, role) if (!instance) { return false }
instance.delete(flush: flush) true }
static void removeAll(User user) { executeUpdate 'DELETE FROM UserRole WHERE user=:user', [user: user] }
static mapping = { id composite: ['role', 'user'] version false } }
It also creates some UI controllers and GSPs:
grails-app/controllers/LoginController.groovy
grails-app/controllers/LogoutController.groovy
grails-app/views/auth.gsp
grails-app/views/denied.gsp
The script has edited grails-app/conf/Config.groovy
and added the configuration for your domain classes. Make sure that the changes are correct.
These generated files are not part of the plugin - these are your application files. They are examples to get you started, so you can edit them as you please. They contain the minimum needed for the plugin.
The plugin has no support for CRUD actions and GSPs for your domain classes; the spring-security-ui
plugin will supply a UI for those. So for now you will create roles and users in grails-app/conf/BootStrap.groovy
. (See step 7.)
$ grails create-controller com.testapp.Secure
This command creates grails-app/controllers/com/testapp/ SecureController.groovy
. Add some output so you can verify that things are working:
package com.testapp
class SecureController { def index = { render 'Secure access only' } }
$ grails run-app
import com.testapp.Role import com.testapp.User import com.testapp.UserRole
class BootStrap {
def init = { servletContext ->
def adminRole = new Role(authority: 'ROLE_ADMIN').save(flush: true) def userRole = new Role(authority: 'ROLE_USER').save(flush: true)
def testUser = new User(username: 'me', enabled: true, password: 'password') testUser.save(flush: true)
UserRole.create testUser, adminRole, true
assert User.count() == 1 assert Role.count() == 2 assert UserRole.count() == 1 } }
Some things to note about the preceding BootStrap.groovy
:
UserRole
class. This performance optimization helps significantly when many users have one or more common roles.BootStrap
does not run in a transaction or OpenSessionInView.
package com.testapp
import grails.plugins.springsecurity.Secured
class SecureController {
@Secured(['ROLE_ADMIN']) def index = { render 'Secure access only' } }
You can annotate the entire controller or individual actions. In this case you have only one action, so you can do either.
This time, you should be presented with the login page. Log in with the username and password you used for the test user, and you should again be able to see the secure page.
$ grails generate-all com.testapp.User
$ grails generate-all com.testapp.Role
Since the User domain class handles password encryption, there are no changes required in the generated controllers.
This isn't a standard step-by-step tutorial since every application is different and the steps required will vary from project to project. Instead these are guidelines and things to keep in mind. You should also readSection 2 and Section 3.
The first thing to do is uninstall the Acegi plugin
$ grails uninstall-plugin acegi
$ grails install-plugin spring-security-core
If this were a new project the next step would be to run the s2-quickstart script but you wouldn't do this for an existing project where you already have a User and Role class, so it's a good idea to work through thebookstore tutorial and use the files generated in that project. The files that the script generates are
grails-app/domain/com/testapp/User.groovy
grails-app/domain/com/testapp/Role.groovy
grails-app/domain/com/testapp/UserRole.groovy
grails-app/controllers/LoginController.groovy
grails-app/controllers/LogoutController.groovy
grails-app/views/login/auth.gsp
grails-app/views/login/denied.gsp
Migrate any changes you made in LoginController.groovy
, LogoutController.groovy
, auth.gsp
and denied.gsp
, and overwrite your files with those. Do the same for User.groovy
and Role.groovy
, and move UserRole.groovy
into your project.
You can use the standard Grails generate-all
script to create a UI to manage Users and Roles as described in the previous tutorial, or for a more complete solution use the Spring Security UI plugin.
The utility service in Spring Security Core is SpringSecurityService
, so you need to replace def authenticateService
with def springSecurityService
. Many of the methods have the same names and signatures but there are some differences:
principal()
was renamed to getPrincipal()
ifAllGranted()
, ifNotGranted()
, and ifAnyGranted()
were removed; use org.codehaus.groovy.grails.plugins.springsecurity. SpringSecurityUtils.ifAllGranted()
, ifNotGranted()
, and ifAnyGranted()
insteadgetSecurityConfig()
was removed, use SpringSecurityUtils.getSecurityConfig()
instead One significant change between the plugins is that the UserDetails
implementation (GrailsUser
) no longer has a reference to the domain class instance. This was intended to make it easy to access User class data that's not available in the Principal
but it has frustrating side effects due to being a disconnected Hibernate object. Instead GrailsUser
stores the user's id so you can conveniently retrieve the instance when needed. So instead of
def user = authenticateService.userDomain() user = User.get(user.id)
use this instead:
def user = User.get(springSecurityService.principal.id)
The Acegi plugin uses a standard Grails many-to-many relationship (i.e. using hasMany
and belongsTo
) between User and Role but this will have performance problems if you have many users. Spring Security Core also uses a many-to-many relationship but maps the join table as a domain class instead of using collections. In the Acegi plugin you would grant a role to a user using
Role role = … User user = … role.addToPeople(user)
and remove the grant with
Role role = … User user = … role.removeFromPeople(user)
In Spring Security Core you use the helper methods in UserRole
Role role = … User user = … UserRole.create user, role
and
Role role = … User user = … UserRole.remove user, role
which directly insert or delete rows in the User/Role join table.
Configuration settings are now stored in grails-app/conf/Config.groovy
along with the rest of the application configuration. The primary motiviation for this change is to easily support environment-specific security settings. Migrate settings from SecurityConfig.groovy
to Config.groovy
(see this summary for the new names.
In particular it's important that the following properties be configured (replace class and package names to match your domain classes):
grails. plugins. springsecurity. userLookup. userDomainClassName = 'com.yourcompany.yourapp.User' grails. plugins. springsecurity. userLookup. authorityJoinClassName = 'com.yourcompany.yourapp.UserRole' grails. plugins. springsecurity. authority. className = 'com.yourcompany.yourapp.Role'
Delete SecurityConfig.groovy
when you're finished.
The Secured
annotation changed from org.codehaus.groovy.grails.plugins. springsecurity.Secured
to grails.plugins.springsecurity.Secured
. Consider using SpEL expressions since they're a lot more powerful and expressive than simple role names.
role
attribute changed to roles
, so for example change
to
instead of
- use
to render other GrailsUser
attributes在Grails项目的Config.groovy中配置如下配置
grails {
plugins {
springsecurity {
active = true
userLookup.userDomainClassName = "com.testapp.User"
authority.className = "com.testapp.Role"
}
}
}
对用的controller代码
LoginControoler
def auth = {
nocache response
if (isLoggedIn()) {
redirect uri: '/'
return
}
String view = 'auth'
String postUrl = "${request.contextPath}${SpringSecurityUtils.securityConfig.apf.filterProcessesUrl}"
render view: view, model: [postUrl: postUrl]
}
auth.gsp
def denied = {
// TODO put any pre-logout code here
redirect(uri: '/j_spring_security_logout')
}