Shiro是一个很不错的安全权限框架,可以完成认证、授权、加密、会话管理、缓存等。既能用于javase,也可以用于JavaEE中。这里,笔者以Javaee中为例,讲解一下shiro的第一部分--认证。
认证,通俗的说,就是我们web系统的登陆。使用shiro框架后,就不用我们向之前那样写较多的对账号密码的逻辑判断校验了,直接调用shiro的相关api即可轻松完成认证。当然,这里面的底层也离不开我们常规的账号密码逻辑判断校验,只是shiro对它们进行了更好地封装与优化。
首先,我们来了解一下shiro的框架的架构,这样有助于理解shiro框架运转流程。从shiro内部来看,如图所示
从架构图中可以看出,shiro最为核心的是security manager(安全管理器),学过springmvc的话,可以理解它为DispatcherServlet的作用。所有与安全有关的操作,都会委托到它这里来。而subject可以理解shiro的门面,与外部代码直接交互的对象,但真正起作用的是它背后的security manager。subject代表的是当前“用户”,这里的用户可以指真正的用户,也可以是机器人、网络爬虫等一切与当前应用交互的对象。security manager里面包含三个很核心的组成部位,一个是Authenticator(认证器)负责subject认证。另一个是Authorizer(授权器)负责控制subject对资源的访问权限。最后一个是Realm,我们可以理解为安全实体数据源,即认证器和授权器进行认证授权时,都会去Realm获取数据。至于其他部件,笔者这里不作讲述,因为,缓存、会话管理等这些在实际开发,有其他方案可以更好的解决,而不采取shiro提供的。
现在我们来使用shiro的认证来实现我们web系统的登陆功能。准备如下:
1.数据表
2.ssm整合的web工程
3.shiro jar包引入
现在开始具体实现。首先,需要在web.xml配置shiro filter拦截器,用来拦截需要安全控制的URL,然后进行相应的控制。这里笔者顺便提醒一下,shiro filter优先于springmvc的拦截器。
shiroFilter
org.springframework.web.filter.DelegatingFilterProxy
targetFilterLifecycle
true
shiroFilter
/*
第二部,配置shiro核心配置文件applicationContext-shiro.xml。配置文件如下
//认证策略
//md5加密
//加密次数
/showLogin.action = anon
/login.action = anon
/logout.action = logout
/user.action = roles[user]
/admin.action = roles[admin]
/list.action = user
# everything else requires authentication:
/** = authc
具体配置,笔者已经在注释写的听清楚了,这里概括一下,这里的配置文件,就是要配置最为核心的security manager以及shiro filter。这里涉及到很多一些shiro的配置细节,比如认证策略、url匹配模式以及shiro的过滤器等,已经在配置文件的注释作了介绍,这里不再展开叙述。
到这里,shiro整合到我们的web应用已经完成了。这里讲一下shiro的认证大致流程,前台页面传递用户名和密码到后台controller,后台利用shiro得到subject,subject调用login方法进行登录。而subject会委托给security manager,security manager委托给Authenticator,Authenticator 可能会委托给相应的AuthenticationStrategy(认证策略) 进行多 Realm 身份验证。Authenticator 会把相应的token传入Realm获取当前用户的认证信息比进行匹配认证。
具体代码如下
@Controller
public class UserController {
@RequestMapping("/login.action")
public String login(@RequestParam("username")String loginName,
@RequestParam("password")String password){
//利用SecurityUtils得到当前用户
Subject subject = SecurityUtils.getSubject();
//如果当前用户未认证过
if (!subject.isAuthenticated()){
//组装token
UsernamePasswordToken token = new UsernamePasswordToken(loginName,password,true);
//设置是否记住我
token.setRememberMe(true);
try {
System.out.println("开始执行登陆");
//调用login方法进行登录
//将会委托给security manager,到realm进行具体的认证匹配
subject.login(token);
}catch (AuthenticationException ae){
ae.printStackTrace();
System.out.println("登录失败: " + ae.getMessage());
return "login";
}
}
//登录成功,跳转成功页面
return "list";
}
}
realm的实现,只需要继承AuthorizingRealm即可。实现两个方法
public class ShiroRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
return null;
}
}
具体认证实现如下
public class ShiroRealm extends AuthorizingRealm {
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//传入的token转回UsernamePasswordToken
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//getPrincipal方法可以获取到唯一身份标识。比如用户名、邮箱等
String principal = (String) upToken.getPrincipal();
//根据传入的唯一身份标识--用户名到数据库查询该用户信息
SysUser user = userService.getUserByLoginNmae(principal);
//唯一身份标识也可以使用查询出来的实体对象
//Object principal = user;
if (user == null){
throw new UnknownAccountException("账户不存在!");
}
//取出从数据库查出来的密码
Object credentials = user.getPassword();
//盐值加密。
//我们之前shiro的配置文件中配置了加密方式为MD5,但是假如有些人的密码是相同的,则加密后的密码
//就会一样的,为了提高安全性,使用盐值加密,这样,即使密码相同,加密后的密码也会不一样,当然参数
//要选择唯一的,比如principal就最好。
ByteSource credentialsSalt = ByteSource.Util.bytes(principal);
//最好构建SimpleAuthenticationInfo对象返回即可
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal,credentials,credentialsSalt,getName());
return info;
}
@Autowired
private UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
补充一点,shiro提供异常有如下所示,可根据具体情况而抛出。
到这里,认证就基本实现了。最后总结一下认证步骤,配置shiro文件以及web.xm的lshiro拦截器(这一步是只要是想要使用shiro都要做的),编写realm类,调用login方法。
关于shiro的权限控制,笔者将在下一篇中详细介绍。