当前位置:主页 > 查看内容

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

发布时间:2021-05-28 00:00| 位朋友查看

简介:shiroSpringBootMybatis-plus 一、pom.xml 二、application.properties 三、shiro的配置文件 四、html文件 五、后端各层文件 六、将项目改造成前后端完全分离 首先创建springboot项目导入相关依赖。 项目结构 访问流程 项目启动类 SpringBootApplication Map……


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

项目结构:

在这里插入图片描述

访问流程:
在这里插入图片描述项目启动类:

@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;
    }
}
;原文链接:https://blog.csdn.net/weixin_46122805/article/details/115507741
本站部分内容转载于网络,版权归原作者所有,转载之目的在于传播更多优秀技术内容,如有侵权请联系QQ/微信:153890879删除,谢谢!
上一篇:iOS应用安全-专栏总目录(持续更新) 下一篇:没有了

推荐图文

  • 周排行
  • 月排行
  • 总排行

随机推荐