Shiro安全框架

78 阅读3分钟

一、与SpringBoot整合

①:框架整合

1. 创建SpringBoot项目

环境:

  • jdk: 1.8
  • SpringBoot: 2.5.14

image.png

2. 整合MyBatis根据实体类生成表

可查看文章:juejin.cn/post/723432…

按照以上笔记配置后在补充一下代码

  • 依赖MyBatisPlus依赖
<!--mybatis-plus-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.0.5</version>
</dependency>

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-web-starter</artifactId>
    <version>1.9.0</version>
</dependency>
  • 配置文件
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:mapper/*.xml
spring:
  datasource:
    url: jdbc:mysql://您的IP地址:3306/shirodb?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: www.Coke.com
    driver-class-name: com.mysql.cj.jdbc.Driver
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
  servlet:
    multipart:
      max-file-size: 20MB
      max-request-size: 20MB
shiro:
  loginUrl: /myController/login
  registerUrl: /myController/register
  • 注意以下位置改成自己的路径

image.png

image.png

image.png

  • 启动类
@SpringBootApplication
@MapperScan("com.it.shiro_springboot.mapper")
public class Application {
    
    public static void main (String[] args) {
        SpringApplication.run(Application.class, args);
    }
    
}

image.png

②:登录认证实现

1.创建实体

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id;
    private String name;
    private String pwd;
    private Integer rid;
}

2.创建mapper

@Mapper
public interface UserMapper extends BaseMapper<User> {
    @Select("select * from user where name = #{name}")
    User getUserInfoByName(String name);

    @Insert("insert into user(name, pwd) values(#{name},#{pwd}) ")
    int userRegister(@Param("name") String name, @Param("pwd") String pwd);
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Table (name = "user", comment = "用户表") //设置表名  注释
public class User implements Serializable {
    private static final long serialVersionUID = 9435278593475930L;
    
    @IsAutoIncrement   //自增
    @IsKey             //主键
    @Column (comment = "用户ID")//字段注释
    private Integer id;
    
    /**
     * 姓名
     */
    @Column(name = "name", comment = "用户名", length = 100)
    private String name;
    
    /**
     * 密码
     */
    @Column(name = "pwd", comment = "密码", length = 100)
    private String pwd;
    
    /**
     * 角色编号
     */
    @Column(name = "rid", comment = "角色编号", length = 100)
    private Integer rid;
}

3.创建 service

1.创建接口

public interface UserService {
    // 用户登录
    User getUserInfoByName (String name);    
    
    // 用户注册
    int userRegister(String name, String pwd);
}

2.创建实现类

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    
    @Override
    public User getUserInfoByName (String name) {
       User user = userMapper.getUserInfoByName(name);
        return user;
    }
    
    @Override
    public int userRegister (String name, String pwd) {
     // md5加密
        SimpleHash simpleHash = new SimpleHash("MD5", pwd, "salt", 3);
        return userMapper.userRegister(name, simpleHash.toHex());
    }
    
    
    
}

4.自定义realm

image.png

@Component
public class MyRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;
    
    // 自定义授权方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo (PrincipalCollection principalCollection) {
        return null;
    }
    // 自定义登录认证方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo (AuthenticationToken token) throws AuthenticationException {
        // 1.获取用户身份信息
        String name = token.getPrincipal().toString();
        // 2. 调用业务层获取用户信息(数据库中)
        User user = userService.getUserInfoByName(name);
        // 判断并将数据完成封装
        if (!ObjectUtils.isEmpty(user)) {
            SimpleAuthenticationInfo authInfo = new SimpleAuthenticationInfo(
                  token.getPrincipal(),
                  user.getPwd(),
                  ByteSource.Util.bytes("salt"),
                  token.getPrincipal().toString()
            );
            return authInfo;
        }
        return null;
    }
}

5.编写配置类

image.png

@Configuration
public class ShiroConfig {
    @Autowired
    private MyRealm myRealm;
    
    @Value("${shiro.loginUrl}")
    private String loginUrl;
    
    @Value("${shiro.registerUrl}")
    private String registerUrl;
    
    // SecurityManager
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(){
        // 1.创建 defaultWebSecurityManager 对象
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        // 2. 创建加密对象,并设置相关属性
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        // 3.采用 md5 加密
        matcher.setHashAlgorithmName("md5");
        // 4.迭代加密次数
        matcher.setHashIterations(3);
        // 5. 将加密对象存储到 myRealm 中
        myRealm.setCredentialsMatcher(matcher);
        // 6. 将 myRealm 存入 defaultWebSecurityManager 对象
        manager.setRealm(myRealm);
        // 7. 返回
        return manager;
    }
    
    // 配置 Shiro 内置过滤器拦截范围
    @Bean
    public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
        DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
        // 设置不认证可以访问的资源
        definition.addPathDefinition(loginUrl, "anon");
        definition.addPathDefinition(registerUrl, "anon");
        definition.addPathDefinition("/login", "anon");
        // 设置需要进行登录认证的拦截范围
        definition.addPathDefinition("/**","authc");
        return definition;
    }
}

6.实现controller

@Controller
@Slf4j
@RequestMapping("myController")
public class MyController {
    
    @Autowired
    private UserService userService;
    
    @GetMapping("login")
    @ResponseBody
    public String userLogin(String name, String pwd){
        // 1 获取 Subject 对象
        Subject subject = SecurityUtils.getSubject();
        // 2 封装请求数据到 token 对象中
        UsernamePasswordToken token = new UsernamePasswordToken(name, pwd);
        //3 调用 login 方法进行登录认证
        try {
            subject.login(token);
            return "登录成功!";
        }catch (AuthenticationException e){
            e.printStackTrace();
            log.info("登录失败");
            return "登录失败!";
        }
        
    }
    
    @GetMapping("register")
    @ResponseBody
    public String register(String name, String pwd){
        int i = userService.userRegister(name, pwd);
        if (i > 0){
            return "注册成功!";
        }else {
            return "注册失败!";
        }
    }
}

7.测试

1.启动程序前数据库中并没有表

image.png

2.启动程序(表即可创建成功)

image.png

image.png

3.注册用户

image.png

4.登录

image.png

③:多个 realm 的认证策略设置

④:remember me 功能

⑤:用户登录认证后登出

⑥:授权、角色认证

⑤:实现缓存

⑥:会话管理