使用shiro进行登录密码安全验证

时间:2022-08-31 11:16:56

使用shiro进行登录密码安全验证

使用框架版本

  • SpringBoot 1.5.3.RELEASE
  • shiro-spring 1.2.5
  • shiro-ehcache 1.2.5

Shiro配置

  • ShiroConfig 中shiroFilter
    /**
* ShiroFilter<br/>
* 注意这里参数中的 StudentService 和 IScoreDao 只是一个例子,因为我们在这里可以用这样的方式获取到相关访问数据库的对象,
* 然后读取数据库相关配置,配置到 shiroFilterFactoryBean 的访问规则中。实际项目中,请使用自己的Service来处理业务逻辑。
*
* @param securityManager
* @return
* @author Scan
* @create 2017年6月14日
*/

@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {

ShiroFilterFactoryBean shiroFilterFactoryBean = new MShiroFilterFactoryBean();
// 必须设置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
// 登录成功后要跳转的连接
shiroFilterFactoryBean.setSuccessUrl("/back/index");
shiroFilterFactoryBean.setUnauthorizedUrl("/403");

loadShiroFilterChain(shiroFilterFactoryBean);

return shiroFilterFactoryBean;
}
    /**
* 加载shiroFilter权限控制规则
*
* @author Scan
* @create 2017年6月14日
*/

private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean){
// 下面这些规则配置最好配置到配置文件中
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
/**
* authc:该过滤器下的页面必须验证后才能访问,
* 它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter
*/


//<!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
//<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->


//配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
/**
* anon(匿名) org.apache.shiro.web.filter.authc.AnonymousFilter
* authc(身份验证) org.apache.shiro.web.filter.authc.FormAuthenticationFilter
* authcBasic(http基本验证) org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
* logout(退出) org.apache.shiro.web.filter.authc.LogoutFilter
* noSessionCreation(不创建session) org.apache.shiro.web.filter.session.NoSessionCreationFilter
* perms(许可验证) org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
* port(端口验证) org.apache.shiro.web.filter.authz.PortFilter
* rest (rest方面) org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
* roles(权限验证) org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
* ssl (ssl方面) org.apache.shiro.web.filter.authz.SslFilter
* member (用户方面) org.apache.shiro.web.filter.authc.UserFilter
* user 表示用户不一定已通过认证,只要曾被Shiro记住过登录状态的用户就可以正常发起请求,比如rememberMe
*/

filterChainDefinitionMap.put("/toLogin", "anon");
filterChainDefinitionMap.put("/signOut", "logout");
filterChainDefinitionMap.put("/back/**", "authc");
filterChainDefinitionMap.put("/**", "anon"); //anon 可以理解为不拦截

shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
}

这样我们就可以设置需要拦截的路径,需要注意不要将登录和退出登录的路径进行拦截

  • 编写自己的Realm进行登录验证和授权
public class MyShiroRealm extends AuthorizingRealm {

private static final Logger logger = LoggerFactory.getLogger(MyShiroRealm.class);

@Resource
private IUserService userService;


/**
* 登录认证
* 身份认证
*/

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//UsernamePasswordToken对象用来存放提交的登录信息
UsernamePasswordToken token=(UsernamePasswordToken) authenticationToken;

logger.info("验证当前Subject时获取到token为:" + ReflectionToStringBuilder.toString(token, ToStringStyle.MULTI_LINE_STYLE));

//查出是否有此用户
User u = new User();
u.setUserName(token.getUsername());
User user = userService.findOne(u);
if(user!= null){
// 若存在,将此用户存放到登录认证info中,无需自己做密码对比,Shiro会为我们进行密码对比校验
return new SimpleAuthenticationInfo(user.getUserName(), user.getPassWord(),ByteSource.Util.bytes(user.getUserName()),user.getName());
}
return null;
}



/**
* 此方法调用 hasRole,hasPermission的时候才会进行回调.
*
* 权限信息.(授权):
* 1、如果用户正常退出,缓存自动清空;
* 2、如果用户非正常退出,缓存自动清空;
* 3、如果我们修改了用户的权限,而用户不退出系统,修改的权限无法立即生效。
* (需要手动编程进行实现;放在service进行调用)
* 在权限修改后调用realm中的方法,realm已经由spring管理,所以从spring中获取realm实例,
* 调用clearCached方法;
* :Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。
* @param principalCollection
* @return
*/

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
logger.info("======================执行Shiro权限认证======================");
//获取当前登录输入的用户名,等价于(String) principalCollection.fromRealm(getName()).iterator().next();
String loginName = (String)super.getAvailablePrincipal(principalCollection);
//到数据库查是否有此对象
User u = new User();
u.setUserName(loginName);
User user = userService.findOne(u);
// 这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
if(user == null){
return null;
}
// 返回null的话,就会导致任何用户访问被拦截的请求时,都会自动跳转到unauthorizedUrl指定的地址
//权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission)
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
//用户的角色集合
info.setRoles(user.getRolesName());
//用户的角色对应的所有权限,如果只使用角色定义访问权限,下面的四行可以不要
Set<Role> roles=user.getRoles();
for (Role role : roles) {
info.addStringPermissions(role.getPermissionsName());
}
return info;
}


}
  • LoginController 核心代码 路径 /login
 // 获取当前subject
Subject currentUser = SecurityUtils.getSubject();
// 测试当前的用户是否已经被认证,即是否已经登陆
// 调用Subject的isAuthenticated
if (!currentUser.isAuthenticated()) {
// 把用户名和密码封装为UsernamePasswordToken 对象
Md5Hash mh = new Md5Hash(validLogin.getPassWord(), validLogin.getUserName(), 3);
UsernamePasswordToken token = new UsernamePasswordToken(validLogin.getUserName(), mh.toString());
token.setRememberMe(true);
try {
// 执行登陆
currentUser.login(token);

// 将当前用户信息放入session
User u = new User();
u.setUserName(validLogin.getUserName());
SecurityUtils.getSubject().getSession().setAttribute("user",userService.findOne(u));

} catch ( UnknownAccountException e ) {
System.out.println("用户未注册!");
} catch ( IncorrectCredentialsException e ) {
System.out.println("密码错误!!");
} catch ( LockedAccountException e ) {
System.out.println("该账户不可用~");
} catch ( ExcessiveAttemptsException e ) {
System.out.println("尝试次数超限!!");
}
}
  • 登出核心代码 路径 /signOut
 @RequestMapping("signOut")
public String signOut(HttpServletRequest request){

Subject currentUser = SecurityUtils.getSubject();
// 退出登录
currentUser.logout();

return "login";
}

到目前为止使用shiro进行简单的登陆和登出操作就完成了,shiro菜鸟学习记录.