Basic and Digest Authentication
Property | Default | Description |
---|---|---|
useBasicAuth |
| Whether to use Basic authentication |
basic.realmName | “Grails Realm” | Realm name displayed in the browser authentication popup |
basic.credentialsCharset | “UTF-8” | The character set used to decode Base64-encoded data |
如果使用Basic Authentication,那么会显示标准浏览器的登录对话框而不会跳到登录页面。
grails.plugin.springsecurity.filterChain.chainMap = [
[pattern: '/webservice/**', filters: 'JOINED_FILTERS,-exceptionTranslationFilter'],
[pattern: '/**', filters: 'JOINED_FILTERS,-basicAuthenticationFilter,-basicExceptionTranslationFilter']
]
Digest Authentication和Basic类似,但是它更安全,因为经过了hash。
Property | Default Value | Meaning |
---|---|---|
useDigestAuth |
| Whether to use Digest authentication |
digest.realmName | “Grails Realm” | Realm name displayed in the browser popup |
digest.key | “changeme” | Key used to build the nonce for authentication; it should be changed but that’s not required |
digest.nonceValiditySeconds |
| How long a nonce stays valid |
digest.passwordAlreadyEncoded |
| Whether you are managing the password hashing yourself |
digest.createAuthenticatedToken |
| If |
digest.useCleartextPasswords |
| If |
Certificate(X.509) Login Authentication
这需要HTTPS,需要服务端配置一个客户端的证书, 如果证书有效,可以从证书中提取到用户名,只要数据库中有这个用户,就可以免密登录 。
Property | Default Value | Meaning |
---|---|---|
useX509 |
| Whether to support certificate-based logins |
x509.continueFilterChainOnUnsuccessfulAuthentication |
| Whether to proceed when an authentication attempt fails to allow other authentication mechanisms to process the request |
x509.subjectDnRegex | “CN=(.*?)(?:,|$)” | Regular expression for extracting the username from the certificate’s subject name |
x509.checkForPrincipalChanges |
| Whether to re-extract the username from the certificate and check that it’s still the current user when a valid |
x509.invalidateSessionOnPrincipalChange |
| Whether to invalidate the session if the principal changed (based on a |
x509.subjectDnClosure | none | If set, the plugin’s |
x509.throwExceptionWhenTokenRejected |
| If |
Remember-Me Cookie
这是一个可选项,通过勾选框可以选择是否使用cookie。
Property | Default Value | Meaning |
---|---|---|
rememberMe.cookieName |
| remember-me cookie name; should be unique per application |
rememberMe.cookieDomain | none | remember-me cookie domain |
rememberMe.alwaysRemember |
| If |
rememberMe.tokenValiditySeconds |
| Max age of the cookie in seconds |
rememberMe.parameter |
| Login form remember-me checkbox name |
rememberMe.key |
| Value used to encode cookies; should be unique per application |
rememberMe.useSecureCookie | none | Whether to use a secure cookie or not; if |
rememberMe.createSessionOnSuccess |
| Whether to create a session of one doesn’t exist to ensure that the |
rememberMe.persistent |
| If |
rememberMe.persistentToken.domainClassName | none | Domain class used to manage persistent logins |
rememberMe.persistentToken.seriesLength | 16 | Number of characters in the cookie’s |
rememberMe.persistentToken.tokenLength | 16 | Number of characters in the cookie’s |
atr.rememberMeClass | remember-me authentication class |
ajaxLogin.js
var onLogin;
$.ajaxSetup({
beforeSend: function(jqXHR, event) {
if (event.url != $("#ajaxLoginForm").attr("action")) {
// save the 'success' function for later use if
// it wasn't triggered by an explicit login click
onLogin = event.success;
}
},
statusCode: {
// Set up a global Ajax error handler to handle 401
// unauthorized responses. If a 401 status code is
// returned the user is no longer logged in (e.g. when
// the session times out), so re-display the login form.
401: function() {
showLogin();
}
}
});
function showLogin() {
var ajaxLogin = $("#ajaxLogin");
ajaxLogin.css("text-align", "center");
ajaxLogin.jqmShow();
}
function logout(event) {
event.preventDefault();
$.ajax({
url: $("#_logout").attr("href"),
method: "POST",
success: function(data, textStatus, jqXHR) {
window.location = "/";
},
error: function(jqXHR, textStatus, errorThrown) {
console.log("Logout error, textStatus: " + textStatus +
", errorThrown: " + errorThrown);
}
});
}
function authAjax() {
$("#loginMessage").html("Sending request ...").show();
var form = $("#ajaxLoginForm");
$.ajax({
url: form.attr("action"),
method: "POST",
data: form.serialize(),
dataType: "JSON",
success: function(json, textStatus, jqXHR) {
if (json.success) {
form[0].reset();
$("#loginMessage").empty();
$("#ajaxLogin").jqmHide();
$("#loginLink").html(
'Logged in as ' + json.username +
' (<a href="' + $("#_logout").attr("href") +
'" id="logout">Logout</a>)');
$("#logout").click(logout);
if (onLogin) {
// execute the saved event.success function
onLogin(json, textStatus, jqXHR);
}
}
else if (json.error) {
$("#loginMessage").html('<span class="errorMessage">' +
json.error + "</error>");
}
else {
$("#loginMessage").html(jqXHR.responseText);
}
},
error: function(jqXHR, textStatus, errorThrown) {
if (jqXHR.status == 401 && jqXHR.getResponseHeader("Location")) {
// the login request itself wasn't allowed, possibly because the
// post url is incorrect and access was denied to it
$("#loginMessage").html('<span class="errorMessage">' +
'Sorry, there was a problem with the login request</error>');
}
else {
var responseText = jqXHR.responseText;
if (responseText) {
var json = $.parseJSON(responseText);
if (json.error) {
$("#loginMessage").html('<span class="errorMessage">' +
json.error + "</error>");
return;
}
}
else {
responseText = "Sorry, an error occurred (status: " +
textStatus + ", error: " + errorThrown + ")";
}
$("#loginMessage").html('<span class="errorMessage">' +
responseText + "</error>");
}
}
});
}
$(function() {
$("#ajaxLogin").jqm({ closeOnEsc: true });
$("#ajaxLogin").jqmAddClose("#cancelLogin");
$("#ajaxLoginForm").submit(function(event) {
event.preventDefault();
authAjax();
});
$("#authAjax").click(authAjax);
$("#logout").click(logout);
});
ajaxLogin.css
#ajaxLogin {
padding: 0px;
text-align: center;
display: none;
}
#ajaxLogin .inner {
width: 400px;
padding-bottom: 6px;
margin: 60px auto;
text-align: left;
border: 1px solid #aab;
background-color: #f0f0fa;
-moz-box-shadow: 2px 2px 2px #eee;
-webkit-box-shadow: 2px 2px 2px #eee;
-khtml-box-shadow: 2px 2px 2px #eee;
box-shadow: 2px 2px 2px #eee;
}
#ajaxLogin .inner .fheader {
padding: 18px 26px 14px 26px;
background-color: #f7f7ff;
margin: 0px 0 14px 0;
color: #2e3741;
font-size: 18px;
font-weight: bold;
}
#ajaxLogin .inner .cssform p {
clear: left;
margin: 0;
padding: 4px 0 3px 0;
padding-left: 105px;
margin-bottom: 20px;
height: 1%;
}
#ajaxLogin .inner .cssform input[type="text"],
#ajaxLogin .inner .cssform input[type="password"] {
width: 150px;
}
#ajaxLogin .inner .cssform label {
font-weight: bold;
float: left;
text-align: right;
margin-left: -105px;
width: 150px;
padding-top: 3px;
padding-right: 10px;
}
.ajaxLoginButton {
background-color: #efefef;
font-weight: bold;
padding: 0.5em 1em;
display: -moz-inline-stack;
display: inline-block;
vertical-align: middle;
white-space: nowrap;
overflow: visible;
text-decoration: none;
-moz-border-radius: 0.3em;
-webkit-border-radius: 0.3em;
border-radius: 0.3em;
}
.ajaxLoginButton:hover, .ajaxLoginButton:focus {
background-color: #999999;
color: #ffffff;
}
#ajaxLogin .inner .login_message {
padding: 6px 25px 20px 25px;
color: #c33;
}
#ajaxLogin .inner .text_ {
width: 120px;
}
#ajaxLogin .inner .chk {
height: 12px;
}
.errorMessage {
color: red;
}
_ajaxLogin.gsp
<span id="logoutLink" style="display: none;">
<g:link elementId='_logout' controller='logout'>Logout</g:link>
</span>
<span id="loginLink" style="position: relative; margin-right: 30px; float: right">
<sec:ifLoggedIn>
Logged in as <sec:username/> (<g:link elementId='logout' controller='logout'>Logout</g:link>)
</sec:ifLoggedIn>
<sec:ifNotLoggedIn>
<a href="#" onclick="showLogin(); return false;">Login</a>
</sec:ifNotLoggedIn>
</span>
<div id="ajaxLogin" class="jqmWindow" style="z-index: 3000;">
<div class="inner">
<div class="fheader">Please Login..</div>
<form action="${request.contextPath}/login/authenticate" method="POST"
id="ajaxLoginForm" name="ajaxLoginForm" class="cssform" autocomplete="off">
<p>
<label for="username">Username:</label>
<input type="text" class="text_" name="username" id="username" />
</p>
<p>
<label for="password">Password</label>
<input type="password" class="text_" name="password" id="password" />
</p>
<p>
<label for="remember_me">Remember me</label>
<input type="checkbox" class="chk" id="remember_me" name="remember-me"/>
</p>
<p>
<input type="submit" id="authAjax" name="authAjax"
value="Login" class="ajaxLoginButton" />
<input type="button" id="cancelLogin" value="Cancel"
class="ajaxLoginButton" />
</p>
</form>
<div style="display: none; text-align: left;" id="loginMessage"></div>
</div>
</div>
main.gsp
<html lang="en" class="no-js">
<head>
...
<g:layoutHead/>
</head>
<body>
<g:render template='/includes/ajaxLogin'/>
...
<g:layoutBody/>
</body>
</html>
Ajax login工作流
(1)单击连接来显示登录form
(2)填写验证信息,单击登录
(3)使用Ajax请求进行提交表单
(4)如果验证成功:
- 重定向url:/login/ajaxSuccess
- 返回json串,串中有一个布尔值success=true,及username
- 客户端决定登录成功,更新page显示用户已登录
(5)如果验证失败
- 重定向/login/authfail?ajax=true
- 返回json串中包含一个string,包含错误信息
- 客户端决定登录失败,更新page显示用户登录失败。
最后欢迎大家访问我的个人网站:1024s