Apache Shiro(三)shiro+SpringBoot+Mybatis-plus

首先创建springboot项目,导入相关依赖。

项目结构:

Apache Shiro(三)shiro+SpringBoot+Mybatis-plus

访问流程:
Apache Shiro(三)shiro+SpringBoot+Mybatis-plus项目启动类:

@SpringBootApplication @MapperScan(value = "com.fy.dao") public class SbMyShiroApplication { 	public static void main(String[] args) { 		SpringApplication.run(SbMyShiroApplication.class, args); 	} } 

一、pom.xml

<dependencies> 		<dependency> 			<groupId>org.springframework.boot</groupId> 			<artifactId>spring-boot-starter-web</artifactId> 		</dependency>         <dependency>             <groupId>com.baomidou</groupId>             <artifactId>mybatis-plus-boot-starter</artifactId>             <version>3.4.2</version>         </dependency>         <dependency>             <groupId>com.alibaba</groupId>             <artifactId>druid-spring-boot-starter</artifactId>             <version>1.1.14</version>         </dependency>         <dependency>             <groupId>org.apache.shiro</groupId>             <artifactId>shiro-spring-boot-starter</artifactId>             <version>1.4.1</version>         </dependency>  		<!--thymeleaf--> 		<dependency> 			<groupId>org.springframework.boot</groupId> 			<artifactId>spring-boot-starter-thymeleaf</artifactId> 		</dependency> 		<dependency> 			<groupId>com.github.theborakompanioni</groupId> 			<artifactId>thymeleaf-extras-shiro</artifactId> 			<version>2.0.0</version> 		</dependency> 		<dependency> 			<groupId>com.alibaba</groupId> 			<artifactId>fastjson</artifactId> 			<version>1.2.62</version> 		</dependency>   		<dependency> 			<groupId>mysql</groupId> 			<artifactId>mysql-connector-java</artifactId> 			<scope>5.1.47</scope> 		</dependency> 		<dependency> 			<groupId>org.springframework.boot</groupId> 			<artifactId>spring-boot-configuration-processor</artifactId> 			<optional>true</optional> 		</dependency> 		<dependency> 			<groupId>org.projectlombok</groupId> 			<artifactId>lombok</artifactId> 			<optional>true</optional> 		</dependency> 		<dependency> 			<groupId>org.springframework.boot</groupId> 			<artifactId>spring-boot-starter-test</artifactId> 			<scope>test</scope> 		</dependency> 	</dependencies> 

二、application.properties

#配置数据源 spring.datasource.druid.driver-class-name=com.mysql.jdbc.Driver spring.datasource.druid.url=jdbc:mysql://localhost:3306/ssm_shiro spring.datasource.druid.password=root spring.datasource.druid.username=root  #日志 logging.level.com.fy.dao=debug  #映射文件地址 mybatis-plus.mapper-locations=classpath*:/mapper/*.xml 

三、shiro的配置文件

在ssm项目中,shiro的组件创建在spring配置文件中,在springboot项目中,需要自己创建一个配置类 
//这个注解让该类相当于spring的配置文件 @Configuration public class ShiroConfig { //    配置安全管理器     @Bean(value = "SecurityManager")     public DefaultWebSecurityManager getSecurityManager(MyRealm MyRealm){         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //        将realm交给安全管理器管理         securityManager.setRealm(MyRealm);         return securityManager;     }  //  创建MyRealm对象     @Bean(value = "MyRealm")     public MyRealm getMyRealm(HashedCredentialsMatcher CredentialsMatcher){         MyRealm myRealm = new MyRealm(); //        给Realm配置密码匹配器         myRealm.setCredentialsMatcher(CredentialsMatcher);         return myRealm;     }  //    创建密码匹配器     @Bean(value = "CredentialsMatcher")     public HashedCredentialsMatcher getCredentialsMatcher(){         HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();         credentialsMatcher.setHashAlgorithmName("MD5");         credentialsMatcher.setHashIterations(1024);         return credentialsMatcher;     }  //    配置过滤规则     @Bean(value = "shiroFilter")     public ShiroFilterFactoryBean getShiroFilterFactory(SecurityManager SecurityManager){         ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //        设置要过滤的安全管理器         shiroFilterFactoryBean.setSecurityManager(SecurityManager); //         未登录访问的跳转路径         shiroFilterFactoryBean.setLoginUrl("/tologin"); //        登录成功时访问的路径         shiroFilterFactoryBean.setSuccessUrl("/success.html");          HashMap<String, String> hashMap = new HashMap<>();         hashMap.put("/login.html","anon");         hashMap.put("/login","anon");         hashMap.put("/static/**","anon");         hashMap.put("/**","authc"); //        其他需要设置的路径         shiroFilterFactoryBean.setFilterChainDefinitionMap(hashMap);          return shiroFilterFactoryBean;     }      //    配置过滤器  相当于ssm项目中web.xml的给shiro配置过滤器     @Bean     public FilterRegistrationBean filterRegistrationBean(){         FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();         filterRegistrationBean.setName("shiroFilter");         filterRegistrationBean.setFilter(new DelegatingFilterProxy());         filterRegistrationBean.addUrlPatterns("/*");         return filterRegistrationBean;     }      //springboot项目只能使用html页面,使shiro标签库可以在thymeleaf中使用     @Bean     public ShiroDialect shiroDialect() {         return new ShiroDialect();     } } 

四、html文件

springboot项目不支持jsp,只能使用html,要使用thymeleaf模板,需要导入依赖

		<dependency> 			<groupId>org.springframework.boot</groupId> 			<artifactId>spring-boot-starter-thymeleaf</artifactId> 		</dependency> 

1、login.html

<!DOCTYPE html> <html lang="en"> <head>     <meta charset="UTF-8">     <title>Title</title> </head> <body> <form action="/login" method="post">     账号:<input type="text" name="username"/><br>     密码:<input type="text" name="userpwd"/><br>     <input type="submit" value="登陆"/>     <input type="button" value="注册" onclick="location.href='regist'"/> </form> </body> </html> 

2、success.html
在这个页面中需要使用shiro的标签,要在页面中引入shiro的标签,也要在pom.xml中引入依赖

		<dependency> 			<groupId>com.github.theborakompanioni</groupId> 			<artifactId>thymeleaf-extras-shiro</artifactId> 			<version>2.0.0</version> 		</dependency> 
<!DOCTYPE html> <!--引入shiro的标签--> <html lang="en" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"> <head>     <meta charset="UTF-8">     <title>Title</title> </head> <body> 登陆成功  <h1>欢迎来到</h1> <shiro:hasPermission name="user:query">     <a href="/user/query">查询所有用户</a><br> </shiro:hasPermission>  <shiro:hasPermission name="user:update">     <a href="/user/update">修改用户</a><br> </shiro:hasPermission>  <shiro:hasPermission name="user:delete">     <a href="/user/delete">删除用户</a><br> </shiro:hasPermission>  <shiro:hasPermission name="user:insert">     <a href="/user/insert">添加用户</a><br> </shiro:hasPermission>  <shiro:hasPermission name="user:export">     <a href="/user/export">导出用户</a><br> </shiro:hasPermission> </body> </html> 

3、un.html

<!DOCTYPE html> <html lang="en"> <head>     <meta charset="UTF-8">     <title>Title</title> </head> <body> 无权访问 </body> </html> 

五、后端各层文件

1、dao
①UserDao

public interface UserDao extends BaseMapper<User> {     /**      * 根据用户id查询该用户具有的权限      * @param userid      * @return      */     List<String> findPermissionByUserid(Integer userid); } 

2、entity

@Data @NoArgsConstructor @AllArgsConstructor public class User {     private Integer userid;     private String username;     private String userpwd;     private String sex;     private String address;     private String salt; //盐 } 

3、service
UserService接口

public interface UserService{     User selectOne(String username);     List<String> findPermissionByUserid(Integer userid); } 

UserServiceImpl.java

@Service public class UserServiceImpl implements UserService {     @Resource     private UserDao userDao;  //    使用的mybatis-plus中的方法     @Override     public User selectOne(String username) {         QueryWrapper<User> wrapper=new QueryWrapper<>();         wrapper.eq("username",username);         User user = userDao.selectOne(wrapper);         return user;     }     @Override     public List<String> findPermissionByUserid(Integer userid) {          return  userDao.findPermissionByUserid(userid);     } } 

4、user的映射文件
UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.fy.dao.UserDao">      <select id="findPermissionByUserid" resultType="String">         select percode from user_role ur,role_permission rp, permission p         where ur.roleid=rp.roleid and rp.perid=p.perid and ur.userid=#{userid}     </select> </mapper> 

5、Controller层
①LoginController: 登录业务

@Controller public class LoginController {     private UsernamePasswordToken token;      @PostMapping("/login")     public String login(String username,String userpwd){         Subject subject = SecurityUtils.getSubject();         UsernamePasswordToken token = new UsernamePasswordToken(username, userpwd);         try {             subject.login(token);             return "success";         }catch (Exception e){             return "login";         }     } } 

②PageController: 未登录时跳转到login.html

@Controller public class PageController {     @RequestMapping("tologin")     public String tologin(){         System.out.println("");         return "login";     } } 

③UserController:

@RestController @RequestMapping("user") public class UserController {      @GetMapping("query")     @RequiresPermissions("user:query")     public String query(){         return "user:query";     }      @GetMapping("delete")     @RequiresPermissions("user:delete")     public String delete(){         return "user:delete";     }      @GetMapping("update")     @RequiresPermissions("user:update")     public String update(){         return "user:update";     }      @GetMapping("insert")     @RequiresPermissions("user:insert")     public String insert(){         return "user:insert";     }      @GetMapping("export")     @RequiresPermissions("user:export")     public String export(){         return "user:export";     } } 

6、handler层
ExceptionController : 处理异常

@ControllerAdvice public class ExceptionController {     @ExceptionHandler(value = AuthorizationException.class)     public String handler(){         return "un";     } } 

7、realm层
MyRealm.java

public class MyRealm extends AuthorizingRealm {     @Autowired     private UserService userServiceimpl;     @Override     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {         User user =(User) principals.getPrimaryPrincipal();         List<String> permission = userServiceimpl.findPermissionByUserid(user.getUserid());         SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();         if(permission.size()>0){             info.addStringPermissions(permission);         }         return info;     }      @Override     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {         String username = token.getPrincipal().toString();         User user = userServiceimpl.selectOne(username);         if(user!=null){             ByteSource salt = ByteSource.Util.bytes(user.getSalt());             SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getUserpwd(), salt, this.getName());             return info;         }         return null;     } } 

六、将项目改造成前后端完全分离

前后端完成分离,后端返回给访问者的是json数据 

该项目中有三个地方需要改造返回json数据:
1、认证成功或认证失败时
2、出现异常时,例如权限不足时
3、未登录访问时

操作:
1、创建返回的json数据格式
utils层-----》CommonResult.java

@Data @NoArgsConstructor @AllArgsConstructor public class CommonResult {     private Integer code;     private String msg;     private Object data; } 

2、改造Controller

	将Controller层的@Controller都改成@RestController,将@ControllerAdvice改成@RestControllerAdvice 

①、LoginController

@PostMapping("login")     public CommonResult login(String username, String userpwd){         Subject subject = SecurityUtils.getSubject();         UsernamePasswordToken token=new UsernamePasswordToken(username,userpwd);         try {             subject.login(token);//realm的认证功能 //getPrincipl()可以得到SimpleAuthenticationInfo中的数据             Object user = subject.getPrincipal();             return new CommonResult(2000,"登陆成功",user);         }catch (Exception e){             return new CommonResult(5000,"登陆失败",null);         }     } 

②、ExceptionController 异常处理类

@RestControllerAdvice public class MyHandlerException {      @ExceptionHandler(value = UnauthorizedException.class)     public CommonResult unauthorizedException(){         return new CommonResult(5001,"权限不足",null);     } } 

③、未登录访问时返回json有两种改造方式
第一种:改PageController

@RestController public class PageController {     @GetMapping("/tologin")     public CommonResult loginPage(){         return new CommonResult(5002,"请先登录",null);     } } 

第二种:改ShiroConfig文件,创建一个过滤器

ShiroConfig.java中改过滤规则中的内容,加上过滤器配置

    @Bean("shiroFilter")     public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){          ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();          shiroFilterFactoryBean.setSecurityManager(securityManager);          shiroFilterFactoryBean.setLoginUrl("/tologin");          shiroFilterFactoryBean.setSuccessUrl("/success.html");           Map<String,String> map=new HashMap<>();          map.put("/index.html","anon");          map.put("/static/**","anon");          map.put("/login","anon"); //anon表示放行          map.put("/**","authc");          shiroFilterFactoryBean.setFilterChainDefinitionMap(map);            //未登录时的过滤器          Map<String, Filter> filterMap=new HashMap<>();          filterMap.put("authc",new LoginFilter());          shiroFilterFactoryBean.setFilters(filterMap);           return shiroFilterFactoryBean;     } 

在filter层创建一个过滤器:
LoginFilter.java

public class LoginFilter extends FormAuthenticationFilter {      @Override     protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {         response.setContentType("application/json;charset=utf-8");         CommonResult commonResult=new CommonResult(5002,"未登录",null);         response.getWriter().print(JSON.toJSONString(commonResult));         return false;     } } 

版权声明:玥玥 发表于 2021-04-10 3:06:11。
转载请注明:Apache Shiro(三)shiro+SpringBoot+Mybatis-plus | 女黑客导航