关于shiro在SSM环境下的整合(2)

接上文

(题外话:好像hexo对Markdown里Java的渲染不是很好,注解会嵌到其他行去?)

编写Controller

做用户认证,首先得有一个Controller,生成一个User对象。由于研究对象是shiro,先不写实际页面,用一个简单的表单向Controller提交对象。

直接上Controller部分代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Controller
//省略具体URL路径代码
//此处直接传入表单,或者JSON也可以,总之要绑定User对象。
public String login(User user, BindingResult result, Model model, HttpServletRequest request) {

try {
//获取一个Subject,Subject在Shiro框架中是主体,一般场景中就是登陆的用户。
Subject subject = SecurityUtils.getSubject();
//如果是已经登录的(Session中有相应会话),跳转到上一个访问的页面
if (subject.isAuthenticated()) {
return "redirect:/";
}
//如果传入参数不正确,例如密码是空的
if (result.hasErrors()) {
model.addAttribute("error", "参数错误!");
return "login";
}

// 将表单传入的email和密码存为Shiro的Token
UsernamePasswordToken token = new UsernamePasswordToken(user.getEmail(), user.getPwd());
//并且提交Realm验证
subject.login(token);
//如果认证通过,没有抛出异常
final User authUserInfo = um.getUserByEmail(user.getEmail());
request.getSession().setAttribute("userInfo", authUserInfo);
} catch (AuthenticationException e) {
// 身份验证失败
model.addAttribute("error", "用户名或密码错误 !");
return "login";
}
}

先实例化一个subject,校验后调用.login()方法,并传入包含用户名(可以是能用来登陆的其他东西)和密码。
之前在Spring配置中将Realm注入到SecurityManager,因此会跳转到Realm处理。

(出于业务需求的原因,本文暂时不涉及多个Realm的情况,可以参考http://blog.csdn.net/xiangwanpeng/article/details/54802509

编写Realm具体方法之认证

跳转之后,首先进入doGetAuthenticationInfo方法,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String email = String.valueOf(token.getPrincipal());
String password = new String((char[]) token.getCredentials());
//获取参数,并使用参数在数据库中验证,此处um即为Spring注入的UserMapper
final User authc = um.getIdPwdByEmail(email);
//接下来的逻辑可以自由发挥,总之登录失败就要抛出异常,并且由Controller捕捉
if (password.equals(authc.getPwd())){
//此处getname是获取该类的名字
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(email, password, getName());
return authenticationInfo;
}else{
throw new AuthenticationException("用户名或密码错误.");
}
}

这里使用了userMapper在数据库中取验证信息,当然也可以使用userService封装好的方法,随意发挥。

如果登陆失败,可以自己抛出并处理各种异常,shiro自带用户锁定、同IP尝试次数过多等非常丰富的异常。由于是小系统,不做复杂处理。

假如这个方法通过了,我们会return一个包含了用户名(确切讲是用户标识,根据业务场景不同而不同),密码(其他授权信息也可以)以及当前类名的authcInfo对象。

这个对象这里我们没有使用,但是可以用在JSP标签中,参考此文:http://www.sojson.com/blog/144.html

编写Realm具体方法之授权

如果上一个方法没有抛出异常,会进入doGetAuthorizationInfo方法,传入对象则是一个principalCollection。

1
2
3
4
5
6
7
8
9
10
11
12
13
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
String email = String.valueOf(principals.getPrimaryPrincipal());
final User user = um.getUserByEmail(email);
Set set = new HashSet<String>();
if (user.getAdmin() == 1){
set.add("Admin");
}else{
set.add("User");
}
authorizationInfo.setRoles(set);
return authorizationInfo;
}

这个方法的作用,是从数据源中,根据用户标识(邮箱),取出所对应的权限(无论是以字段形式存在于用户实体,或者是另起一张表,都只是形式,目的是要取出权限)。

并且以字符串的set形式,存入authzInfo对象中并且return,return的这个对象稍后可以在JSP中以tag形式调用。也可以直接在Spring配置中写上/admin = authz,使用shiro自带的Filter进行过滤。

至此,使用Shiro在SSM应用中的认证、授权已经完成。这里实现了最简单的功能,系统只有两个角色(用户、管理员),管理员能访问后台系统,用户不能访问,仅此而已。

事实上shiro的功能远远不止这些,多用户、多权限,甚至多数据源、多Realm都可以做到。甚至还提供了密码加盐存储的工具类,而这一切都能和Spring 完美整合。

使用shiro的目的,首先是无需手写Filter拦截每一个没有权限的请求,其次是不必手写代码去判断用户权限,不必手写代码实现每个权限能做的事,也不必手动处理登陆失败的情景,节省大量工作量。


题外话

在之前的学习过程中,我看到的demo为了实现全部功能,将用户、权限全部实现,起了五张表,分别存储用户、权限、角色,用户-角色关系,角色-权限关系。

更丧心病狂的是,权限表还有roleSign,roleName,roleDescription三个字段,而实际上放进authzInfo的只有roleSign一个字符串。

我尝试直接阅读代码,一个Realm三个Service三个实体,让我对功能一头雾水,今早突然醒悟,既然源码都开放了,我何必苦苦猜测功能,直接git clone 打breakpoint debug,模拟一次登录,终于豁然开朗。

关于shiro在SSM环境下的整合(2)

http://blog.mothership.top/posts/2df5fcfd.html

作者

Mother Ship

发布于

2017-07-25

更新于

2023-02-13

许可协议

评论