1.引入依赖
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.5.3</version> </dependency>
2.了解shiro的三大对象(转载至:https://blog.csdn.net/qq_41430393/article/details/87730198):
1.Subject(主体)
应用代码的直接交互对象就是Subject,也就是说Shiro对外的核心API就是Subject,Subject代表了当前“用户”,这个用户不是指具体的某一个人,可以说与当前应用交互的任何东西都是Subject,与Subject的所有交互都会委托给SecurityManager来执行,可以理解为Subject只是一个充当门面的,真正的幕后老大是SecurityManager,SecurityManager才是实际的执行者。
2.SecurityManager(安全管理器)
所有与安全有关的操作都会与SecurityManager进行交互,并且SecurityManager管理者所有的Subject,可以看出它才是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于SpringMvc中的dispatcherServlet(前端控制器)的角色。
3.Realm
Shiro从Realm获取安全数据(用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法,也需要从Realm获取用户的角色权限来判断用户是否能进行一系列操作。可以把Realm看作DataSource数据源.
3.配置shiroConfig类以及shiroReaml类
shiroConfig类(将三大对象联系起来,配置需要拦截的页面):
@Configuration //交由spring管理 public class shiroConfig { //自底向上创建,并绑定 //ShiroFilterFactoryBean,第三步 @Bean public ShiroFilterFactoryBean getFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); //关联安全管理器 bean.setSecurityManager(securityManager); //添加shiro的内置过滤器 /* anon:无需认证就可以访问 authc:必须认证才能访问 验证密码和用户 user:必须拥有 记住我 功能才能用 perms:拥有对某个资源的权限才能访问 role:拥有某个角色权限才能访问 */ //使用map添加相应页面的过滤 Map<String, String> filterMap=new LinkedHashMap<>(); //因为是链式过滤,所以得先过滤范围小的 //不仅需要登陆还得拥有权限才可访问 //表示数据库中user表中的字段perm为user:update才表示有这个权限访问改路径 filterMap.put("/user/update","perms[user:update]"); filterMap.put("/user/add","perms[user:add]"); //需要登陆即可访问的页面 filterMap.put("/user/*","authc"); //设置未登录时,进入需要权限的页面时,跳转的登陆页面 bean.setLoginUrl("/toLogin"); //设置权限不足时跳转的页面 bean.setUnauthorizedUrl("/noAuth"); bean.setFilterChainDefinitionMap(filterMap); return bean; } //DefaultWebSecurityManager,第二步 @Bean(name = "securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("getRealm") shiroRealm shiroRealm){ DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager(); //关联realm securityManager.setRealm(shiroRealm); return securityManager; } //创建realm对象,第一步@Bean交由spring管理 @Bean public shiroRealm getRealm(){ return new shiroRealm(); } }
shiroReaml类(用于实现用户认证和授权的操作)
//授权和认证都会经过此类 public class shiroRealm extends AuthorizingRealm { @Autowired userService service; //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { Subject subject = SecurityUtils.getSubject();//获取当前用户 user currentUser = (user) subject.getPrincipal();//从当前用户中取出Principal(认证时已存储了user) SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();//是AuthorizationInfo的实现类 info.addStringPermission(currentUser.getPerm());//根据数据库中的perm字段添加相应的权限 return info; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; user user = service.findByUsername(token.getUsername()); //用户名校验,查看是否存在用户名 if(user==null){ return null;//返回null表示账号不存在,会抛出异常UnknownAccountException } //密码校验,shiro自带检验 //若是密码错误会抛出异常IncorrectCredentialsException return new SimpleAuthenticationInfo(user,user.getPassword(),""); //第一个参数是principal作用是传递信息的,如认证完后可以将数据库中查询的user放进去, //供授权时,给相应的用户添加相应数据库中的权限 } }
4.配置Controller的实现
@Controller public class myController { @RequestMapping({"/","/index"}) public String index(){ return "index"; } @RequestMapping("/user/add") public String add(){ return "user/add"; } @RequestMapping("/user/update") public String update(){ return "user/update"; } @RequestMapping("/toLogin") public String toLogin(){ return "login"; } @RequestMapping("/login") public String login(String username, String password, Model model){ Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username,password); try { subject.login(token);// 该操作会将token转至手写的realm类中的doGetAuthenticationInfo方法中认证登陆 return "index"; }catch (UnknownAccountException e){//用户名不存在 model.addAttribute("msg","用户名不存在"); return "login"; }catch (IncorrectCredentialsException e){//密码不存在 model.addAttribute("msg","密码错误"); return "login"; } } @RequestMapping("/noAuth") @ResponseBody public String noAuth(){ return "权限不足!"; } @RequestMapping("/logout") @ResponseBody public String logout(){ SecurityUtils.getSubject().logout();//注销 return "注销成功!"; } }
5.shiro整合thymeleaf
a.引入整合的依赖:
<dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency>
b.在shiroConfig类中添加ShiroDialect类
@Bean public ShiroDialect getDialect(){ return new ShiroDialect(); }
c.在前端页面引入thymeleaf整合shiro的命名空间
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
d.将登陆成功的用户保存至shiro的session(与HttpSession不同)
//认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; user user = service.findByUsername(token.getUsername()); //用户名校验,查看是否存在用户名 if(user==null){ return null;//返回null表示账号不存在,会抛出异常UnknownAccountException } //登陆成功将用户放进session Subject currentSubject = SecurityUtils.getSubject(); currentSubject.getSession().setAttribute("loginUser",user); //密码校验,shiro自带检验 //若是密码错误会抛出异常IncorrectCredentialsException return new SimpleAuthenticationInfo(user,user.getPassword(),""); //第一个参数是principal作用是传递信息的,如认证完后可以将数据库中查询的user放进去, //供授权时,给相应的用户添加相应数据库中的权限 }
e.前端页面的逻辑实现
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>首页</h1> <hr> <div th:if="${session.loginUser==null}"> <a th:href="@{/toLogin}">登陆</a> </div> <div th:if="${session.loginUser!=null}"> <a th:href="@{/logout}">点我注销</a> <div th:text="当前用户为+':'+${session.loginUser.Username}"></div> </div> <div shiro:hasPermission="user:add"> <a th:href="@{/user/add}">add</a> </div> <div shiro:hasPermission="user:update"> <a th:href="@{/user/update}">update</a> </div> </body> </html>