setSuccessurl基本无效
这个方法是设置认证成功之后的跳转到的页面,但是试了很多方法都不能跳转。
解决办法是可以在方法返回时重定向到要跳转的页面,但是问题又来了。现在大多都喜欢在请求成功之后给前端一个json格式的返回值,里面带上状态码呀,字符串呀什么的,这样就不方便重定向来跳转页面。
可是如果前端页面只用form表单来往后台传值,后台对应的方法在处理完数据之后会自动跳转到该方法的接口页面
form表单请求成功之后跳转到action请求的路径的页面
举个例子:
前端:
<form action="/login" method="get">
用户名:<input type="text" name="username" id="username">
<br>
密码:<input type="text" name="password" id="password">
<br>
<input type="submit" value="登陆" >
</form>
后台:
@GetMapping("/login")
@ResponseBody
public Map<String,Object> login(String username,String password){
System.out.println(username+"++++++++"+password);
Map<String,Object> map = new HashMap<>();
// 进行身份验证
try {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
// 登陆操作
subject.login(token);
}catch (IncorrectCredentialsException e){
map.put("code",500);
map.put("msg","用户不存在或密码错误");
return map;
}catch (LockedAccountException e){
map.put("code",500);
map.put("msg","登陆失败,用户已被冻结");
return map;
}catch (AuthenticationException e){
map.put("code",500);
map.put("msg","该用户不存在");
return map;
}catch (Exception e){
map.put("code",500);
map.put("msg","未知异常");
return map;
}
map.put("code",200);
map.put("msg","登陆成功");
return map;
}
ShiroConfig中设置successurl
/**
* shiro的基本配置
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager){
// shiro的过滤器
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// securityManager是shiro的控制器,协调其他组件完成认证授权
// 这里为过滤器添加控制器
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 申明过滤器配置的集合
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// 配置shiro默认登录界面地址,前后端分离中登录界面跳转应由前端路由控制,后台仅返回json数据
shiroFilterFactoryBean.setLoginUrl("/toLogin");
// 登录成功的页面
shiroFilterFactoryBean.setSuccessUrl("/index");
// 登录失败的页面
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
// 注意过滤器配置顺序不能颠倒
// 可直接访问的资源
// 这里/login是处理登陆的method
filterChainDefinitionMap.put("/login", "anon");
// 静态资源(前后端分离的项目不需要配置这个)
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
// 未授权禁止访问的资源
filterChainDefinitionMap.put("/**", "authc");
// 对过滤器赋值
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
页面输入账号密码后自动跳转为
这样的一个页面。
从上面ShiroConfig中可以看到,successurl是设置了的,但是跳转的确是 /login的接口的json数据页面。就很离谱。
ajax需要请求两次,认证控制器才能起作用
好!!!
那我给它用ajax来往后台传值,因为前端可以控制页面的跳转(当然是在认证通过之后)
代码如下:
前端:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登陆页面</title>
</head>
<body>
<form action="" method="get">
用户名:<input type="text" name="username" id="username">
<br>
密码:<input type="text" name="password" id="password">
<br>
<input type="submit" value="登陆" onclick="login()">
</form>
</body>
<script src="/js/jquery.js"></script>
<script>
function login()
{
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
$.ajax({
type: 'get',
url: "login",
data: {
"username": username,
"password": password
},
success: function (data) {
console.log(data)
if (data.code == 200){
alert("登录成功!")
window.location.href="index";
}else {
alert("登陆失败!用户名或密码错误")
}
}
});
}
</script>
</html>
这里把form表单中的 acthon不设置接口名,防止form和ajax都传值。
后台把ShiroConfig中的setSuccessurl给注释掉,其他不变。
js中的**window.location.href=“index”;**就可以控制页面的跳转了。
这样看着没问题
可是邪门的问题又来了(也许是我刚入门技术尚浅找不到问题所在)
这是最开始的登陆页面:
输入账号密码后点击登陆
前端页面变成这个样子
然后后台控制台输出为:
可以看到后台是拿到数据了的,最下面的那个对象地址是在ShiroRealm中认证方法中的输出,不多说看代码:
/**
* 身份认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 获取用户输入的账号
String username = (String) authenticationToken.getPrincipal();
System.out.println(username);
// 通过username从数据库中查找 User对象,如果找到则进行验证
TUser user = userService.selectUserByName(username);
// System.out.println(user);
// 判断账号是否存在
if (user == null){
throw new AuthenticationException();
}
// 判断账号是否被冻结
if (user.getState() ==null || user.getState().equals("PROHIBIT")){
throw new LockedAccountException();
}
// 进行验证
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
// 用户名
user,
// 密码
user.getPassword(),
// 盐值
// ByteSource.Util.bytes(user.getSalt()),
getName()
);
System.out.println(user);
return authenticationInfo;
}
可以看到认证管理器是拿到数据了的,但是没起作用。
这里说明一下:为什么说认证管理器没起作用呢,因为我试过在地址栏输localhost:8080/index,自动给我跳到登陆页面,说明没认证。
行!!!我不关服务器再请求一次
点击登陆之后:
跳转成功,同时认证也成功。
服务器的控制台打印数据:
这个问题就特别离谱。
form表单和ajax结合起来用
然后我把form表单和ajax结合起来用,虽然看着特别别扭,既form表单请求了后台,ajax也请求后台,看起来多次一举,但是就离谱的解决了这个问题。
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登陆页面</title>
</head>
<body>
<form action="/login" method="get">
用户名:<input type="text" name="username" id="username">
<br>
密码:<input type="text" name="password" id="password">
<br>
<input type="submit" value="登陆" onclick="login()">
</form>
</body>
<script src="/js/jquery.js"></script>
<script>
function login()
{
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
$.ajax({
type: 'get',
url: "login",
data: {
"username": username,
"password": password
},
success: function (data) {
console.log(data)
if (data.code == 200){
alert("登录成功!")
window.location.href="index";
}else {
alert("登陆失败!用户名或密码错误")
}
}
});
}
</script>
</html>
虽然很不规范,但是问题解决了。
单纯用ajax的话,前端拿不到后台传过来的数据
在后面写注册功能的时候我发现,只用ajax是拿不到后台传过来的json数据的,还是用ajax+form表单,才能解决这个问题!
认证成功,但是授权器没有运行
我用的注解的方式进行授权
这里主要是需要在ShiroConfig中开启Shiro的注解支持
- 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
- 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可实现此功能
代码如下:
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
/**
* 开启aop注解支持
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}