-
Notifications
You must be signed in to change notification settings - Fork 0
Spring Security Core Plugin
Das Spring Security Plugin vereinfacht die Integration von Spring Security in Grails-Anwendungen. Das Plugin bietet sinnvolle Voreinstellungen mit vielen Konfigurationsmöglichkeiten zur Anpassung. Nahezu alles ist im Plugin und in Spring Security selbst, das ausgiebig von Schnittstellen Gebrauch macht, konfigurierbar oder austauschbar. Mehr dazu
Um das Plugin benutzen zu können muss man folgenden Eintrag in die build.gradle einfügen
dependencies {
...
compile 'org.grails.plugins:spring-security-core:4.0.3'
...
}
Um die Domainklassen User und Role hinzuzufügen, über welche man sich später anmeldet, muss man s2-quickstart benutzen. Hier muss man nur noch PACKAGE-NAME durch den Namen des eigenen Packages ersetzen.
s2-quickstart PACKAGE-NAME User Role
Nach dem erfolgreichen Ausführen wurden die Domainklassen User, Role sowie deren Zuordnungstabelle UserRole erstellt.
Nun wird der folgende Code in die Domainklasse Role hinzugefügt
...
String toString() {
authority
}
Nun erweitern wir die User-Domainklasse mit zwei Attributen für den Vornamen und den Nachnamen. Dafür werden in der User-Domainklasse die folgenden Attribute hinzugefügt:
String surname
String prename
Wichtig hierbei ist, dass man kein Attribut mit der Bezeichnung name verwendet, da dies früher oder später zu grösseren Problemen führt.
Nun fügen wir noch die beiden Attribute von oben zu den constraints hinzu. Dafür muss man folgende zeilen einfügen:
static constraints = {
...
surname nullable: false
prename nullable: false
}
Als nächstes erstellt man die Datei src/main/groovy/YourPackageName/CustomUserDetails.groovy
Diese sieht dann wie folgt aus:
import grails.plugin.springsecurity.userdetails.GrailsUser
import org.springframework.security.core.GrantedAuthority
class CustomUserDetails extends GrailsUser {
final String surname
final String prename
CustomUserDetails(String username, String password, boolean enabled,
boolean accountNonExpired, boolean credentialsNonExpired,
boolean accountNonLocked,
Collection<GrantedAuthority> authorities,
long id, String surname, String prename) {
super(username, password, enabled, accountNonExpired,
credentialsNonExpired, accountNonLocked, authorities, id)
this.surname = surname
this.prename = prename
}
}
Hat man dies erledigt, so erstellt man mit Hilfe des Befehls create-service YourPackageName.CustomUserDetails den dazugehörigen Service.
Der Inhalt wird durch die folgenden Zeilen ersetzt:
import grails.plugin.springsecurity.SpringSecurityUtils
import grails.plugin.springsecurity.userdetails.GrailsUserDetailsService
import grails.plugin.springsecurity.userdetails.NoStackUsernameNotFoundException
import grails.gorm.transactions.Transactional
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UsernameNotFoundException
class CustomUserDetailsService implements GrailsUserDetailsService {
static final List NO_ROLES = [new SimpleGrantedAuthority(SpringSecurityUtils.NO_ROLE)]
UserDetails loadUserByUsername(String username, boolean loadRoles)
throws UsernameNotFoundException {
return loadUserByUsername(username)
}
@Transactional(readOnly=true, noRollbackFor=[IllegalArgumentException, UsernameNotFoundException])
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = User.findByUsername(username)
if (!user) throw new NoStackUsernameNotFoundException()
def roles = user.authorities
def authorities = roles.collect {
new SimpleGrantedAuthority(it.authority)
}
return new CustomUserDetails(user.username, user.password, user.enabled,
!user.accountExpired, !user.passwordExpired,
!user.accountLocked, authorities ?: NO_ROLES, user.id,
user.surname, user.prename)
}
}
Damit man den neu erstellten Service benutzen kann, muss dieser noch in der Datei grails-app/conf/spring/resources.groovy hinzugefügt werden.
beans = {
...
userDetailsService(CustomUserDetailsService)
}
Damit man in der NavBar einen Reiter Account hat, unter dem man sich anmelden bzw. abmelden kann muss man die Datei grails-app/views/layout/main.gsp verändern. Dafür sucht man die folgende Stelle im Code
<nav class="navbar navbar-expand-lg navbar-dark navbar-static-top" role="navigation">
...
<div class="collapse navbar-collapse" aria-expanded="false" style="height: 0.8px;" id="navbarContent">
...
</div>
</nav>
und ersetzt den Teil
<div class="collapse navbar-collapse" aria-expanded="false" style="height: 0.8px;" id="navbarContent">
...
</div>
durch
<div class="collapse navbar-collapse" aria-expanded="false" style="height: 0.8px;" id="navbarContent">
<ul class="nav navbar-nav ml-auto">
<g:pageProperty name="page.nav"/>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbardropAccount" data-toggle="dropdown">
Account
</a>
<div class="dropdown-menu navbar-dark">
<sec:ifLoggedIn>
<a><sec:loggedInUserInfo field='prename'/> <sec:loggedInUserInfo field='surname'/></a>
<a href="/logoff">Logout</a>
</sec:ifLoggedIn>
<sec:ifNotLoggedIn>
<g:link controller='login' action='auth'>Login</g:link>
</sec:ifNotLoggedIn>
</div>
</li>
</ul>
</div>
Nun wird mit dem Befehl create-controller YourPackageName.Register der Controller für den Registriervorgang erstellt.
Dieser wird dann wieder durch den folgenden Code ersetzt:
import grails.validation.ValidationException
import grails.gorm.transactions.Transactional
import grails.plugin.springsecurity.annotation.Secured
@Transactional
class RegisterController {
static allowedMethods = [register: "POST"]
def index() { }
def register() {
if(!params.password.equals(params.repassword)) {
flash.message = "Password and Re-Password not match"
redirect action: "index"
return
} else {
try {
def user = User.findByUsername(params.username)?: new User(username: params.username, password: params.password, prename: params.prename, surname: params.surname).save()
def role = Role.get(params.role.id)
if(user && role) {
UserRole.create user, role
UserRole.withSession {
it.flush()
it.clear()
}
flash.message = "You have registered successfully. Please login."
redirect controller: "login", action: "auth"
} else {
flash.message = "Register failed"
render view: "index"
return
}
} catch (ValidationException e) {
flash.message = "Register Failed"
redirect action: "index"
return
}
}
}
}
Nun muss noch die dazugehörige Webseite erstellen, damit man sich auch registrieren kann. Dafür wird die Datei grails-app/views/register/ erstellt.
Auch hier muss man den unten stehenden Code hinzufügen
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<meta name="layout" content="${gspLayout ?: 'main'}"/>
<title>Register</title>
</head>
<body>
<div class="row">
<div class="col-sm-9 col-md-7 col-lg-5 mx-auto">
<div class="card card-signin my-5">
<div class="card-body">
<h5 class="card-title text-center">Register Here</h5>
<g:if test='${flash.message}'>
<div class="alert alert-danger" role="alert">${flash.message}</div>
</g:if>
<form class="form-signin" action="register" method="POST" id="loginForm" autocomplete="off">
<div class="form-group">
<label for="role">Role</label>
<g:select class="form-control" name="role.id"
from="${deputatecalculation.Role.list()}"
optionKey="id" />
</div>
<div class="form-group">
<label for="username">Username</label>
<input type="text" placeholder="Your username" class="form-control" name="username" id="username" autocapitalize="none"/>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" placeholder="Your password" class="form-control" name="password" id="password"/>
</div>
<div class="form-group">
<label for="password">Re-Enter Password</label>
<input type="password" placeholder="Re-enter password" class="form-control" name="repassword" id="repassword"/>
</div>
<div class="form-group">
<label for="username">Prename</label>
<input type="text" placeholder="Your prename" class="form-control" name="prename" id="prename" autocapitalize="none"/>
</div>
<div class="form-group">
<label for="username">Surname</label>
<input type="text" placeholder="Your surname" class="form-control" name="surname" id="surname" autocapitalize="none"/>
</div>
<button id="submit" class="btn btn-lg btn-primary btn-block text-uppercase" type="submit">Register</button>
<hr class="my-4">
<p>Already have an account? <g:link controller="login" action="auth">Login</g:link></p>
</form>
</div>
</div>
</div>
</div>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function(event) {
document.forms['loginForm'].elements['username'].focus();
});
</script>
</body>
</html>
Um nun bei den Controllern die Berechtigungen für die einzelnen Gruppen anzupassen, muss man diese Controller mit dem Befehl generate-all YourPackageName.ControllerName -force erstellen.
Nun kann man die Berechtigungen mit Hilfe der @Secured() Annotation vergeben. Diese kann über einzelne Methoden sowie über die gesamte Klasse geschrieben werden.
Ein Beispiel dafür ist der RegisterController. Möchte man die Registrierung eines neuen Accounts nur für eine Bestimmte Rolle Freigeben, so kann man dies wie folgt erledigen
@Secured('ROLE_ADMIN') //Nur Benutzern mit der Rolle "ROLE_ADMIN" das Registrieren eines Accounts erlauben
class RegisterController {
...
}
Man kann auch mehrere Rollen festlegen, indem man @Secured(['ROLE_USER', 'ROLE_ADMIN', '...']) benutzt.
Möchte man nur einzelne Methoden für bestimmte Rollen freigeben, so setzt man diese Annotation direkt über die Methode
@Secured(['ROLE_USER', 'ROLE_ADMIN'])
def show(Long id) {
...
}
This wiki was created using Grails version 4.0.9 and Java 1.8.0_281!
The wiki was last updated with the release of the Tag v0.1.3-PrototypeForRating!