Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。上个月写了一个在线教育的项目用到了shiro权限控制,这几天又复盘了一下,对其进行了深入探究,来总结一下。
一、实现功能
1、完成了记住我功能
2、完成了密码加密功能
3、完成了根据shiro权限访问不同内容
4、完成了使用shiro的session进行保存
二、实现代码
1、引入shiro相关的依赖
我是前端使用了thymeleaf,所以需要引入下面shiro相关的依赖
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency>
2、自定义密码验证器
(1)这个类实现了shiro的SimpleCredentialsMatcher接口来重新密码验证方法
(2)同时写了加密的方法encrypt(String data),拿到前台传过来的密码后,使用该方法加密后与数据库的拿到的密码进行比对,返回ture或者fasle。
(3)当然,前台注册时,保存数据库的密码也需要用同样的形式把密码加密后再保存。
//验证密码 查找到了1该用户 自定义密码验证器 public class MyMatcher extends SimpleCredentialsMatcher { @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token; String pwd = encrypt(String.valueOf(usernamePasswordToken.getPassword())); String mysqlpwd = (String) info.getCredentials(); return this.equals(pwd, mysqlpwd); } //将传进来的密码进行加密的方法 private String encrypt(String data){ String sha384Hex=new Sha384Hash(data).toBase64(); return sha384Hex; } }
3、自定义realm
当密码验证通过后,就到了我们的自定义realm,在我们自定义realm中实现了AuthorizingRealm接口,将其方法进行重写,将各种权限对用户进行授权,同时对用户身份进行验证,代码如下,每一行代码具体含义十分详细了。
//登录及权限验证 public class MyRealm extends AuthorizingRealm { @Autowired UserService us; //角色权限和对应权限添加 //Authorization授权,将数据库中的角色和权限授权给输入的用户名 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //获取登录的用户名 String phone = (String) principalCollection.getPrimaryPrincipal(); //到数据库里查询要授权的内容 User user = us.querybyname(phone); //记录用户的所有角色和权限 SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();//权限信息 for(Role r:user.getRoles()){ //将所有的角色信息添加进来。 simpleAuthorizationInfo.addRole(r.getRname()); for(Permission p:r.getPermissions()){ //将此次遍历到的角色的所有权限拿到,添加·进来 simpleAuthorizationInfo.addStringPermission(p.getPname()); } } return simpleAuthorizationInfo; } //用户身份验证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //从token获取用户名,从主体传过来的认证信息中获取 //加这一步的目的是在post请求时会先进入认证然后再到请求。 if(authenticationToken.getPrincipal()==null){ return null; } //获取用户的登录信息,用户名 String phone=authenticationToken.getPrincipal().toString(); //根据service调用用户名,查找用户的全部信息 //通过用户名到数据库获取凭证 User user=us.querybyname(phone); if(user==null){ //这里返回会报出对应异常 return null; }else{ //这里验证authenticationToken和simpleAuthenticationInfo的信息 SimpleAuthenticationInfo simpleAuthenticationInfo=new SimpleAuthenticationInfo(phone,user.getUpwd().toString(),getName()); return simpleAuthenticationInfo; } }
4、自定义记住我过滤器
该过滤器是为了实现记住我后,用户再次登陆不需要进行权限验证,就能到达首页,后面会介绍当不使用该过滤器的后果。
(1)该过滤器实现当用户通过isRemembered()登陆,没有通过isAuthenticated()登陆时拿到user的session信息,保证后面到达首页时候能拥有各种跟通过isAuthenticated()登陆时的session信息。
(2)过滤器完成了登陆条件的过滤,要么通过权限认证登陆成功,要么通过记住我登陆成功。
(3)在shiroconfig类中会进行shiro访问权限配置。
public class MyRememberFilter extends FormAuthenticationFilter { protected boolean isAccessAllowed(HttpServletRequest request, HttpServletResponse response, Object mappedValue){ Subject subject=getSubject(request,response); if(!subject.isAuthenticated() && subject.isRemembered()){ if(subject.getSession().getAttribute("user")==null &&subject.getPrincipal()!=null){ subject.getSession().setAttribute("user",subject.getPrincipal()); } } return subject.isAuthenticated() || subject.isRemembered(); } }