介绍两种比较常见的认证方法。适用于不同的场景
1、传统的认证 (适用于客户端、服务器端直接跳转页面的情况)
java后台能直接跳转的应用
配置如下:
application.xml中配置
/Captcha.jpg = anon
/styles/** = anon
/Captcha.jpg = anon
/login/timeout = anon
/login = authc
/logout = logout
/** = user
在java controller中配置
@Controller
@RequestMapping("/login")
public class LoginController {
private static final String LOGIN_PAGE = "login";
@RequestMapping(method = RequestMethod.GET)
public String login(HttpServletRequest request) {
return LOGIN_PAGE;
}
@RequestMapping(method = RequestMethod.POST)
public String fail(String username,Map map, HttpServletRequest request) {
String msg = parseException(request);
map.put("msg", msg);
map.put("username", username);
return LOGIN_PAGE;
}
private String parseException(HttpServletRequest request) {
String error = (String) request
.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);
String msg = "其他错误!";
if (error != null) {
if ("org.apache.shiro.authc.UnknownAccountException".equals(error))
msg = "未知帐号错误!";
else if ("org.apache.shiro.authc.IncorrectCredentialsException"
.equals(error))
msg = "密码错误!";
else if ("com.ketayao.security.shiro.IncorrectCaptchaException"
.equals(error))
msg = "验证码错误!";
else if ("org.apache.shiro.authc.AuthenticationException"
.equals(error))
msg = "认证失败!";
else if ("org.apache.shiro.authc.DisabledAccountException"
.equals(error))
msg = "账号被冻结!";
}
return "登录失败," + msg;
}
}
以上的认证过程为:
当在login.jsp 中 点击登录按钮,POST请求 /login {username:"admin", password: "123456"}
首先 进入 /login = authc 认证过滤器,将请求来的username password 在自定义的Realm进行认证,
如果认证通过,则走
如果认证失败,走继续走/login 的action,也就是LoginController中的fail()方法
如果上面的认证过程不是很明白,则可以参考源码。
看一下AdviceFilter的源码吧:
public abstract class AdviceFilter extends OncePerRequestFilter{
public void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
throws ServletException, IOException {
Exception exception = null;
try {
boolean continueChain = preHandle(request, response);
if (log.isTraceEnabled()) {
log.trace("Invoked preHandle method. Continuing chain?: [" + continueChain + "]");
}
if (continueChain) {
executeChain(request, response, chain);
}
postHandle(request, response);
if (log.isTraceEnabled()) {
log.trace("Successfully invoked postHandle method");
}
} catch (Exception e) {
exception = e;
} finally {
cleanup(request, response, exception);
}
}
}
当前面所讲的认证通过后,boolean continueChain= preHandle(request,response);
continueChain会返回false。
所以下面的代码是不执行的。
if (continueChain) {
executeChain(request, response, chain);
}
但是认证失败时,返回true,就会执行自己的action操作。
2、自定义认证(适用于ajax类的json操作,非web应用)
第二种配置相比之下就较灵活 ,首先配置applicationContext-shiro.xml
/ = anon
/views/** = anon
/styles/** = anon
/login = anon
/login/timeout = anon
/Captcha.jpg = anon
/logout = logout
/** = user
咋看一下,几乎没什么区别,主要的一个区别是:/login = authc 这句配置没有了。也就是说,认证的过程由自己控制了。
先看一下LoginController中的代码
@RequestMapping(value = "/login", method = RequestMethod.POST)
public @ResponseBody String login(String username,String password,HttpServletRequest request) {
UsernamePasswordToken token = new UsernamePasswordToken( username, password );
token.setRememberMe(true);
Subject currentUser = SecurityUtils.getSubject();
String messageCode = MessageCode.LOGIN_SUCCESS ;
try {
currentUser.login(token);
} catch ( UnknownAccountException ex ) {
messageCode = MessageCode.LOGIN_ACCOUNT_ERROR;
ex.printStackTrace();
} catch ( IncorrectCredentialsException ex ) {
messageCode = MessageCode.LOGIN_PASSWORD_ERROR;
ex.printStackTrace();
} catch ( LockedAccountException ex ) {
messageCode = MessageCode.LOGIN_ACCOUNT_LOCKED;
ex.printStackTrace();
} catch ( ExcessiveAttemptsException ex ) {
messageCode = MessageCode.LOGIN_EXCESSIVE_ERROR;
ex.printStackTrace();
} catch ( AuthenticationException ex ) {
messageCode = MessageCode.LOGIN_AUTHTICATION_FAIL;
ex.printStackTrace();
}
if(currentUser.isAuthenticated()){
return new ResultVO<>(messageCode).toString(); //认证成功
}else{
return new ResultVO<>(ResultVO.FAILURE,messageCode).toString(); //认证失败
}
}
来进行验证,然后 在try{} catch(){} 语句中,得到相应的错误。
根据下面的判断,来返回json数据给前端
if(currentUser.isAuthenticated()){
return new ResultVO<>(messageCode).toString(); //认证成功
}else{
return new ResultVO<>(ResultVO.FAILURE,messageCode).toString(); //认证失败
}
前端就能根据返回的json数据,做出自己的选择。也就实现了 前后台的隔离。
与第一种验证方法还是有些不同,第一种方法 是后台进行逻辑跳转。
而且第二种方法也能适用于非web应用的程序。