在上一章已经搭建了Web环境,模拟了登录退出的场景,现在我们使用shiro框架来做登录管理
在pom.xml中添加spring-shiro依赖
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency>
Shiro 中的Realm 作用相当与数据源,AuthorizingRealm 的功能是提供用户验证信息和用户权限信息。在这里我们继承AuthorizingRealm 实现doGetAuthenticationInfo方法,登录用户是“admin”时,这创建SimpleAuthenticationInfo返回。SimpleAuthenticationInfo 中要包含对应的验证字段,Shiro会将SimpleAuthenticationInfo和登录请求参数做对比,如果没有匹配登录失败,如果方法返回空也同样登录失败。
public class MyShiroRealm extends AuthorizingRealm { /** * 用于判断登录信息的方法 */ protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken authcToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authcToken; String name = token.getUsername(); //String password = String.valueOf(token.getPassword()); if ("admin".equals(name)) { return new SimpleAuthenticationInfo(name, "123456", getName()); } else { return null; } } /** * 这里是用来返回用户权限的方法 */ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null; } }
Shiro 在Web中是以filter的模式工作的,在Shiro中主要的验证判断是交给SecurityManager 完成的,所以我们需要创建SecurityManager 并将自定义的Realm 配置进去,然后将SecurityManager 配置到ShiroFilterFactoryBean 中。
再说ShiroFilterFactoryBean 这个类会对应我们配置的参数生成filter,对匹配的url进行拦截判断
@Configuration public class ShiroConfig { /** * Shiro 的使用中只有一个SecurityManager,用于管理所有的安全操作 * 这里生成SecurityManager 并配置Realm */ @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(new MyShiroRealm()); return securityManager; } /** * 这个方法生成ShiroFilter的工厂类,因为Shiro要起作用,就需要配置到Spring的过滤器链中。 * 这样shiro就会拦截到请求进行处理 */ @Bean public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必须设置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); //拦截器. Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); //配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了 filterChainDefinitionMap.put("/logout", "logout"); //配置静态资源放行 filterChainDefinitionMap.put("/favicon.ico", "anon"); filterChainDefinitionMap.put("/static/**", "anon"); //<!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了; //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问--> filterChainDefinitionMap.put("/**", "authc"); // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面 shiroFilterFactoryBean.setLoginUrl("/login"); // 登录成功后要跳转的链接 shiroFilterFactoryBean.setSuccessUrl("/index"); //未授权界面; //shiroFilterFactoryBean.setUnauthorizedUrl("/403"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } }
现在我们的登录已经交给Shiro管理了,所有需要修改我们的Controller了,首先/logout不需要了,其次在登录成功之后,浏览器会持有对应的Cookie,所以如果登录了就不需要在进入login页面了,最后如果登录成功Shiro会直接转跳到登录成功的url,如果等失败会继续执行我们的登录函数,这时可以从request 中获取到shiro发现的异常类型。
@Controller public class HomeController { @RequestMapping(value = {"/", "/index"}, method = RequestMethod.GET) public String index() { return "index"; } /** * 如果已经登录了,直接转跳index */ @RequestMapping(value = "/login", method = RequestMethod.GET) public String loginGet(Model model) { if (ShiroUtils.getSubject().isAuthenticated()) { return "redirect:index"; } else { model.addAttribute("msg", "请登录"); return "login"; } } /** * 登录成功会直接按配置转跳 * 登录失败的时候会进入到这个函数里,可以提取出登录报错 */ @RequestMapping(value = "/login", method = RequestMethod.POST) public String loginPost(HttpServletRequest request, Model model) { Object exception = request.getAttribute("shiroLoginFailure"); String msg = "登录失败"; if (exception != null) { if (UnknownAccountException.class.getName().equals(exception)) { msg = "用户名不正确,请重新输入"; } else if (IncorrectCredentialsException.class.getName().equals(exception)) { msg = "密码错误,请重新输入"; } else { msg = "发生未知错误,请联系管理员。"; } } model.addAttribute("msg", msg); return "login"; } }
浙公网安备 33010602011771号