Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
org.springframework.boot spring-boot-starter-parent2.5.3
org.springframework.boot spring-boot-starter-web
package com.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @Description: SpringBoot启动类 */ @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
package com.example.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; /** * @Description:控制层 */ @Controller public class TestController { /** * 测试方法 */ @GetMapping("/hello") @ResponseBody public String hello(){ System.out.println("UserController.hello()"); return "ok"; } }
org.springframework.boot spring-boot-starter-thymeleaf
/** * 测试thymeleaf */ @RequestMapping("/test") public String testThymeleaf(Model model){ //把数据存入model model.addAttribute("name", "张三"); //返回test.html return "test"; }
Title
org.apache.shiro shiro-spring1.10.1
package com.example.shiro; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; /** * @Description:自定义Realm 处理登录 权限 */ public class UserRleam extends AuthorizingRealm { /** * 执行授权逻辑 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("执行授权逻辑"); return null; } /** * 执行认证逻辑 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("执行认证逻辑"); return null; } }
package com.example.shiro; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; /** * @Description:shiro配置类 */ @Configuration public class ShiroConfig { /** * 创建ShiroFilterFactoryBean */ @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //设置安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager); //添加Shiro内置过滤器 /** * Shiro内置过滤器,可以实现权限相关的拦截器 * 常用的过滤器: * anon: 无需认证(登录)可以访问 * authc: 必须认证才可以访问 * user: 如果使用rememberMe的功能可以直接访问 * perms: 该资源必须得到资源权限才可以访问 * role: 该资源必须得到角色权限才可以访问 */ MapfilterMap = new LinkedHashMap (); filterMap.put("/hello", "anon"); filterMap.put("/login", "anon"); filterMap.put("/**", "authc"); //要求登陆时的链接,非必须。 shiroFilterFactoryBean.setLoginUrl("/login"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap); return shiroFilterFactoryBean; } /** * 创建DefaultWebSecurityManager */ @Bean public DefaultWebSecurityManager getDefaultWebSecurityManager(UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //关联realm securityManager.setRealm(userRealm); return securityManager; } /** * 创建Realm */ @Bean public UserRealm getRealm(){ return new UserRealm(); } }
登陆页面 登录
/** * 登陆页面跳转 */ @GetMapping("/login") public String login(){ return "login"; } /** * 登录逻辑处理 */ @PostMapping("/login") public String login(String username,String password,Model model){ /** * 使用Shiro编写认证操作 */ //1.获取Subject Subject subject = SecurityUtils.getSubject(); //2.封装用户数据 UsernamePasswordToken token = new UsernamePasswordToken(username,password); //3.执行登录方法 try { subject.login(token); //登录成功 //跳转到test.html return "redirect:/test"; } catch (UnknownAccountException e) { //e.printStackTrace(); //登录失败:用户名不存在 System.out.println("用户名不存在"); return "login"; }catch (IncorrectCredentialsException e) { //e.printStackTrace(); //登录失败:密码错误 System.out.println("密码错误"); return "login"; } }
package com.example.shiro; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; /** * @Description:自定义Realm 处理登录 权限 */ public class UserRealm extends AuthorizingRealm { /** * 执行授权逻辑 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("执行授权逻辑"); return null; } /** * 执行认证逻辑 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("执行认证逻辑"); //假设数据库的用户名和密码 String username = "aaa"; String password = "123"; //编写shiro判断逻辑,判断用户名和密码 //1.判断用户名 UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken; if(!token.getUsername().equals(username)){ //用户名不存在 return null;//shiro底层会抛出UnKnowAccountException } //2.判断密码 /** * 对比密码 * 参数1:主体对象,按需要传,登陆成功后该参数可通过SecurityUtils.getSubject().getPrincipal()获取。 * 参数2:从对象中取密码,users.getPassword()是这个用户的数据库中的密码 是用来和authenticationToken里的密码比对 * 参数3:盐,可以为空 * 参数4:当前realm的名字 */ return new SimpleAuthenticationInfo("flk好帅", password,null, getName()); } }
由于在shiro过滤器中添加了filterMap.put(“/hello”, “anon”);,所以无需认证(登录)就可以访问/hello
由于在shiro过滤器中添加了filterMap.put(“/**”, “authc”);,所以必须认证才可以访问/test,页面便跳转到登陆页面/login
可以正常进入到test.html。
控制台
账号或密码不正常,根据controller层的判断逻辑会跳转到登陆页面/login
com.alibaba druid-spring-boot-starter1.2.15 mysql mysql-connector-javaorg.mybatis.spring.boot mybatis-spring-boot-starter2.1.3
在src/main/resources目录下创建application.yml文件,并添加配置
spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/test username: root password: admin type: com.alibaba.druid.pool.DruidDataSource # MyBatis mybatis: # 搜索指定包别名 type-aliases-package: com.example.domain # 配置mapper的扫描,找到所有的mapper.xml映射文件 mapperLocations: classpath*:mapper/**/*Mapper.xml # 日志配置 logging: level: com.example: debug
package com.example.domain; /** * @Description:User类 */ public class User { private Integer id; private String username; private String password; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
package com.example.mapper; import com.example.domain.User; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; /** * @Description:Usermapper */ public interface UserMapper { User findByUsername(@Param("username") String username); }
在src/main/resources/mapper目录下
package com.example.service; import com.example.domain.User; /** * @Description:IUserService */ public interface IUserService { /** * 根据用户名查询用户 * @param username 用户名 * @return */ User findByUsername (String username); }
package com.example.service.impl; import com.example.domain.User; import com.example.mapper.UserMapper; import com.example.service.IUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @Description:UserServiceImpl */ @Service public class UserServiceImpl implements IUserService { @Autowired private UserMapper userMapper; @Override public User findByUsername(String username) { return userMapper.findByUsername(username); } }
package com.example; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @Description: SpringBoot启动类 */ @SpringBootApplication //@MapperScan指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类 @MapperScan("com.example.mapper") public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
package com.example.shiro; import com.example.domain.User; import com.example.service.IUserService; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.springframework.beans.factory.annotation.Autowired; /** * @Description:自定义Realm 处理登录 权限 */ public class UserRealm extends AuthorizingRealm { @Autowired private IUserService userService; /** * 执行授权逻辑 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("执行授权逻辑"); return null; } /** * 执行认证逻辑 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("执行认证逻辑"); //假设数据库的用户名和密码 // String username = "aaa"; // String password = "123"; //编写shiro判断逻辑,判断用户名和密码 //1.判断用户名 UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken; //从数据库中获取用户信息 User user = userService.findByUsername(token.getUsername()); if(user == null){ //用户名不存在 return null;//shiro底层会抛出UnKnowAccountException } //2.判断密码 /** * 对比密码 * 参数1:主体对象,按需要传,登陆成功后该参数可通过SecurityUtils.getSubject().getPrincipal()获取。 * 参数2:从对象中取密码,users.getPassword()是这个用户的数据库中的密码 是用来和authenticationToken里的密码比对 * 参数3:盐,可以为空 * 参数4:当前realm的名字 */ return new SimpleAuthenticationInfo("flk好帅", user.getPassword(),null, getName()); } }
org.springframework.boot spring-boot-starter-aop
/** * 开启Shiro注解 */ @Bean public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor( DefaultWebSecurityManager defaultWebSecurityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager); return authorizationAttributeSourceAdvisor; }
/** * 执行授权逻辑 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("执行授权逻辑"); //给资源进行授权 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //假设数据库的aaa用户的权限只有user:add info.addStringPermission("user:add"); //info.addStringPermission("user:update"); return info; }
add addPage
update updatePage
Title 进入添加页面: 添加
进入更新功能: 更新
/** * 添加页面跳转 */ //需要user:add权限才能访问 @RequiresPermissions("user:add") @GetMapping("/add") public String add(){ return "add"; } /** * 添加页面跳转 */ //需要user:update权限才能访问 @RequiresPermissions("user:update") @GetMapping("/update") public String update(){ return "update"; }
成功进入,因为它有user:add这个权限。
– 控制台打印:
失败进入,因为它没有user:update这个权限。
– 控制台打印:
(后续可以做全局异常捕获跳转到提示页面,我这边没有去弄。。。)
添加thymeleaf与shiro的扩展
com.github.theborakompanioni thymeleaf-extras-shiro2.0.0
配置ShiroDialect,用于thymeleaf和shiro标签配合使用
/** * 配置ShiroDialect,用于thymeleaf和shiro标签配合使用 */ @Bean public ShiroDialect getShiroDialect(){ return new ShiroDialect(); }
Title 进入添加页面: 添加
进入更新功能: 更新
控制台打印: